home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / m2 / cat3src / cat / groupsel.i < prev    next >
Text File  |  1997-10-26  |  85KB  |  2,744 lines

  1. (* Konzept fr neue Gruppenauswahl und neue interne Gruppenverwaltung 
  2.  * 
  3.  * Es gibt ein Modul, das eine Gruppenliste verwaltet. Diese Gruppenliste
  4.  * wird aus einer eigenen Datei aufgebaut, die von CAT erstellt wird.
  5.  * Die Datei beinhaltet folgendes:
  6.  *
  7.  * Lokalen Gruppennamen
  8.  * Netzgruppennamen
  9.  * Chef der Gruppe
  10.  * Beschreibung der Gruppe
  11.  * Follow-Up to (erst ab Maus 9 im ITG)
  12.  * Sysop-Info fr die Gruppe
  13.  * Status der Gruppe (aus ITG)
  14.  * Lesereihenfolge (diese Gruppe an x-ter Stelle zu lesen)
  15.  * interne Gruppennummer
  16.  *
  17.  * Diese Datei wird von CAT aus dem ITG und der Gruppen.INF erstellt. 
  18.  * Wenn die Datei nicht existiert, dann wird sie neu erstellt, wobei dann 
  19.  * die Lesereihenfolge nicht festgelegt ist. 
  20.  * Wenn ein ITG neu kommt, dann wird die Datei mit der neuen ITG abgeglichen 
  21.  * und ggf. korrigiert. Ebenso, wenn neue Gruppen angelegt werden.
  22.  * 
  23.  * Das Modul, daž diese Liste verwaltet, stellt Routinen zur Verfgung, um
  24.  * auf der Liste zu arbeiten und Teillisten fr verschiedene Funktionen
  25.  * zur Verfgung zu haben. Es stellt mindestens folgende Listen zur Verfgung:
  26.  *
  27.  * Komplette Gruppenliste fr Gruppenauswahl beim An-/Abstellen 
  28.  * Gruppenliste der in CAT vorhandenen Gruppen
  29.  * Gruppenliste mit Chefstatus
  30.  * Gruppenliste mit allen Gruppen mit Schreiberlaubnis (fr neue Nachrichten)
  31.  * 
  32.  * Datenkonsistenz:
  33.  *
  34.  * Die Datei muž immer dann bearbeitet werden, wenn eine neue Gruppe in CAT
  35.  * ankommt, ein neues ITG (oder IGK!) kommt oder die Lesereihenfolge ge„ndert
  36.  * wird. Es ist darauf zu achten, daž auch CatPutz diese Datei beim L”schen
  37.  * einer Gruppe mit pflegt. 
  38.  *
  39.  * Damit die Gruppenliste nur in diesem Modul verwaltet wird, mssen Funktionen
  40.  * fr alle m”glichen Operationen vorhanden sein, auch zum L”schen von Eintr„gen.
  41.  * Auch das Umbenennen von Gruppen sollte m”glich sein. Auch die Datei GRUPPEN.INF
  42.  * sollte danach nur noch von diesem Modul behandelt werden. Sollte keine ITG 
  43.  * von der Box kommen, so muž das Modul auch mit der Datei IGK arbeiten k”nnen,
  44.  * allerdings nur eingeschr„nkt.
  45.  *
  46.  * Definitiv durch dieses Modul beeinflužt wird data, da dort bis jetzt die
  47.  * Gruppenliste verwaltet wurde. Ebenfalls ge„ndert werden muž das Modul
  48.  * GruppenAuswahl, da diese natrlich die Informationen aus dem ITG untersttzen
  49.  * soll.
  50.  *
  51.  * Ggf. weitere Anforderungen stellen sich dann w„hrend der Implementation 
  52.  * heraus.
  53.  *)
  54. IMPLEMENTATION MODULE GroupSelect;
  55.  
  56. (* Aus Platzgrnden werden bei den Namen nur Pointer gespeichert und ein 
  57.  * Block alloziert, der alle Namen enth„lt.
  58.  *)
  59.  
  60. FROM SYSTEM     IMPORT ADDRESS, ADR, TSIZE, CADR, ASSEMBLER, CALLSYS;
  61.  
  62. (* MM2-Module *)
  63. FROM Storage    IMPORT ALLOCATE, DEALLOCATE;
  64. IMPORT Block, BinOps, Lists, Strings, StrConv;
  65. FROM Characters         IMPORT CR, LF, SUB;
  66.  
  67. (* Magic-Module *)
  68. IMPORT MagicStrings, mtTextfiles, mtAppl, MagicDOS;
  69.  
  70. (* CAT-Module *)
  71. FROM CatTypes   IMPORT Str255Ptr;
  72. FROM Void       IMPORT v;
  73. FROM MTPaths    IMPORT DataPath;
  74.  
  75. IMPORT CatTypes, Infofiles, MTE, QuickSort, CatFiles, dataSys, ConfVars, 
  76.        Varnames, AssFuncs, CatLog, data, Protokoll;
  77.  
  78.  
  79. CONST   (* Magics fr Header *)
  80.         groupCatMagic     = 43415447H;  (* "CATG"                    *)
  81.         groupVersion      = 4;          (* Versionsnummer der CATGROUP.DAT *)
  82.         groupVersionMagic = 0104H;      (* Versionsmagic                   *)
  83.  
  84.  
  85. CONST   groupFileName   = 'catgroup.inf';
  86.         groupDataName   = 'catgroup.dat';
  87.         renameFileName  = 'REN.txt';
  88.  
  89. TYPE
  90.     groupHdr = RECORD
  91.                 name,                    (* Offsets in Data hinein, der  *)
  92.                 netname,                 (* erste ist immer 0. Ansonsten heižt *)
  93.                 chef,                    (* ein offset von 0, daž das Feld nicht *)
  94.                 kurztext,                (* vorhanden ist *)
  95.                 sysop,
  96.                 followup,
  97.                 herkunft,
  98.                 sprache,
  99.                 alias     : CARDINAL;
  100.                 readNumber: INTEGER;
  101.                 catNumber : INTEGER;
  102.                 readState : rwState;      (* F-Zeile *)
  103.                 writeState: rwState;      (* F-Zeile *)
  104.                 userState : uState;       (* F-Zeile *)
  105.                 isNet     : netState;     (* F-Zeile *)
  106.                 hasProg   : BOOLEAN;      (* F-Zeile *)
  107.                 withDollar: BOOLEAN;      (* F-Zeile *)
  108.                 selected  : BOOLEAN;      (* Fr Darstellung in Liste, wird beim Einlesen auf FALSE gesetzt *)
  109.                 ordered   : orderState;   (* Fr An-/Abbestellen von Gruppen *)
  110.                 orderDays : INTEGER;      (* Madness-Erweiterung: Gruppe soviel Tage zurck bestellen *)
  111.                 dataLength: CARDINAL;     (* Die L„nge des folgenden Datenblocks *)
  112.                END;
  113.  
  114. TYPE mayCopyProc = PROCEDURE ( (* entry: *) groupPtr): BOOLEAN;
  115.  
  116.      groupArrayType = ARRAY [0..$7FFF] OF group;
  117.      
  118.      groupArrayPtr  = POINTER TO groupArrayType;
  119.  
  120. VAR listModified: BOOLEAN;
  121.     
  122.     groupArray  : groupArrayPtr;
  123.     maxGroups   : INTEGER;
  124.     groups      : INTEGER;
  125.     
  126.     fullList    : BOOLEAN;
  127.     isSysop     : BOOLEAN;
  128.  
  129. PROCEDURE MakeNewGroupArray (force : BOOLEAN; new : CARDINAL) : BOOLEAN;
  130. (* Alloziert ein neues Gruppenarray und kopiert das alte hinein.
  131.  * FALSE: Nicht genug Speicher!
  132.  *)
  133.  VAR i :  INTEGER;
  134.      newMax : CARDINAL;
  135.      newArray : groupArrayPtr;
  136. BEGIN
  137.   IF (maxGroups = 0) OR (groups >= maxGroups - 1) OR force
  138.   THEN
  139.     IF force
  140.     THEN
  141.       newMax := new + 50;
  142.     ELSE
  143.       newMax := maxGroups + 50;
  144.     END;
  145.     ALLOCATE (newArray, LONG(newMax) * TSIZE (group));
  146.     IF newArray = NIL
  147.     THEN RETURN FALSE END;
  148.     Block.Clear (newArray, LONG(newMax)*TSIZE(group));
  149.     IF groupArray # NIL 
  150.     THEN
  151.       FOR i := 0 TO INTEGER(maxGroups) - 1 DO newArray^[i] := groupArray^[i] END;
  152.     END;
  153.     DEALLOCATE (groupArray, 0);
  154.     maxGroups := newMax; 
  155.     groupArray := newArray;
  156.   END;
  157.   RETURN TRUE;
  158. END MakeNewGroupArray;
  159.  
  160. PROCEDURE ParseFlags (VAR entry: group; REF flags: ARRAY OF CHAR);
  161.   VAR i, l : INTEGER;
  162.       rwEntry : rwState;
  163. BEGIN
  164.   l := LENGTH (flags);
  165.   i := 0;
  166.   WHILE i < l DO
  167.     CASE flags [i] OF 
  168.       'L', 
  169.       'S'  : CASE flags[i+1] OF
  170.                 '+' : rwEntry := rwOn; |
  171.                 'B' : rwEntry := rwPossible; |
  172.                 'C' : rwEntry := rwAskChef; |
  173.                 '-' : rwEntry := rwImpossible; |
  174.                 'P' : rwEntry := rwDefault; |
  175.                 'S' : rwEntry := rwAskSysop; |
  176. (*                'N' : rwEntry := rwNoPermission; | *)
  177.              ELSE
  178.                rwEntry := rwNA;
  179.              END;
  180.              IF flags[i] = 'L'
  181.              THEN
  182.                entry.readState := rwEntry;
  183.              ELSE
  184.                entry.writeState := rwEntry;
  185.              END; |
  186.       'V'  : IF flags[i+1] = '+' THEN
  187.                entry.isNet := nNet;
  188.              ELSIF flags[i+1] = '-' THEN
  189.                entry.isNet := nLokal;
  190.              ELSIF flags[i+1] = '=' THEN
  191.                entry.isNet := nNetLokal;
  192.              ELSIF flags[i+1] = '!' THEN
  193.                entry.isNet := nNetForbid;
  194.              ELSE
  195.                entry.isNet := nNA;
  196.              END |
  197.       'G'  : IF flags[i+1] = '*' THEN
  198.                entry.userState := uIsChef;
  199.              ELSIF flags[i+1] = '+' THEN
  200.                entry.userState := uIsUser;
  201.              ELSIF flags[i+1] = '-' THEN
  202.                entry.userState := uNoUser;
  203.              ELSE
  204.                entry.userState := uNA;
  205.              END; |
  206.       'P'  : entry.hasProg := flags[i+1] = '+'; |
  207.       '$'  : entry.withDollar := flags[i+1] = '+'; |
  208.     ELSE
  209.     END;
  210.     INC (i, 2);
  211.   END;
  212. END ParseFlags;
  213.  
  214.   TYPE lineEntry = (gL, nL, uL, cL, fL, sL, rL, hL, pL, aL, cN, rN, oN, dN);
  215.        lineSet   = SET OF lineEntry;
  216.  
  217. VAR compName : CatTypes.String255;
  218.  
  219. PROCEDURE findEntry (REF name: ARRAY OF CHAR; VAR idx: INTEGER): BOOLEAN;
  220. (* Bin„re Suche im Array *)
  221.   VAR left, right, mid : INTEGER;
  222.       found : BOOLEAN;
  223.       res   : MagicStrings.Relation;
  224. BEGIN
  225.   found := FALSE;
  226.   left := 0; right := groups-1;
  227.   WHILE (left <= right) & ~found DO
  228.     (* Beware of overflows! *)
  229.     mid := SHORT((LONG(left) + LONG(right)) DIV 2);
  230.     res := AssFuncs.StrICompare (name, groupArray^[mid].name^);
  231.     IF res = MagicStrings.equal
  232.     THEN
  233.       found := TRUE; idx := mid;
  234.     ELSIF res = MagicStrings.greater
  235.     THEN
  236.       left := mid + 1
  237.     ELSE
  238.       right := mid - 1
  239.     END;
  240.   END;
  241.   IF ~found
  242.   THEN
  243.     (* den kleineren Wert zuweisen, dahinter wird dann ggf. eingefgt *)
  244.     idx := right;
  245.   END;
  246.   RETURN found;
  247. END findEntry;
  248.  
  249. PROCEDURE findNetEntry (REF name: ARRAY OF CHAR; VAR idx: INTEGER): BOOLEAN;
  250.   VAR i : INTEGER;
  251. BEGIN
  252.   FOR i := 0 TO groups - 1 DO
  253.     IF groupArray^[i].catNumber > 0
  254.     THEN
  255.       IF (groupArray^[i].netname # NIL) &
  256.          AssFuncs.StrIequal (name, groupArray^[i].netname^)
  257.       THEN
  258.         idx := i;
  259.         RETURN TRUE
  260.       END;
  261.     END;
  262.   END;
  263.   RETURN FALSE
  264. END findNetEntry;
  265.  
  266. PROCEDURE countCatEntries (VAR count: INTEGER);
  267.   VAR i : INTEGER;
  268. BEGIN
  269.   count := 0;
  270.   FOR i := 0 TO groups - 1 DO 
  271.     IF groupArray^[i].catNumber >= 0 THEN INC (count) END;
  272.   END;
  273. END countCatEntries;
  274.  
  275. PROCEDURE findNumberEntry (number : CARDINAL; VAR idx: CARDINAL): BOOLEAN;
  276.   VAR i : INTEGER;
  277. BEGIN
  278.   FOR i := 0 TO groups - 1 DO
  279.     IF groupArray^[i].catNumber = INTEGER(number)
  280.     THEN
  281.       idx := i;
  282.       RETURN TRUE
  283.     END;
  284.   END;
  285.   RETURN FALSE
  286. END findNumberEntry;
  287.  
  288. PROCEDURE findReadNumberEntry (number : CARDINAL; VAR idx: CARDINAL): BOOLEAN;
  289.   VAR i : INTEGER;
  290. BEGIN
  291.   FOR i := 0 TO groups - 1 DO
  292.     IF groupArray^[i].readNumber = INTEGER(number)
  293.     THEN
  294.       idx := i;
  295.       RETURN TRUE
  296.     END;
  297.   END;
  298.   RETURN FALSE
  299. END findReadNumberEntry;
  300.  
  301. PROCEDURE MakeEntry (gr: groupPtr; VAR gName, nName, uLine, cLine, fLine, sLine, rLine,
  302.                      hLine, pLine, aLine : ARRAY OF CHAR;
  303.                      readNum, catNum : INTEGER; oState: orderState; days : INTEGER; 
  304.                      defaultFlags: BOOLEAN; avail : lineSet): BOOLEAN;
  305. (* Fllt den Eintrag gr mit den Daten 
  306.  *)
  307. VAR gLen,
  308.     nLen,
  309.     uLen,
  310.     sLen,
  311.     rLen,
  312.     cLen,
  313.     hLen,
  314.     pLen,
  315.     aLen    : INTEGER;
  316.     addLen  : INTEGER;
  317.     target  : ADDRESS;
  318. BEGIN
  319.   (* L„ngen herausfinden *)
  320.   gLen := (*SYSTEM.*)LENGTH (gName);
  321.   nLen := (*SYSTEM.*)LENGTH (nName);
  322.   uLen := (*SYSTEM.*)LENGTH (uLine);
  323.   cLen := (*SYSTEM.*)LENGTH (cLine);
  324.   sLen := (*SYSTEM.*)LENGTH (sLine);
  325.   rLen := (*SYSTEM.*)LENGTH (rLine);
  326.   hLen := (*SYSTEM.*)LENGTH (hLine);
  327.   pLen := (*SYSTEM.*)LENGTH (pLine);
  328.   addLen := gLen + nLen + uLen + cLen + sLen + rLen + hLen + pLen + 12;
  329.   (* Jetzt Speicher allozieren *)
  330.   IF gr^.data # NIL THEN DEALLOCATE (gr^.data, 0) END;
  331.   gr^.dataLength := 0;
  332.   ALLOCATE (gr^.data, addLen);
  333.   IF gr^.data = NIL THEN RETURN FALSE END;
  334.   gr^.dataLength := addLen;
  335.   (* Speicher vorhanden, jetzt Eintr„ge setzen *)
  336.   target := gr^.data;
  337.   INC (gLen);
  338.   Block.Copy (ADR(gName), gLen, target);
  339.   gr^.name := target;
  340.   INC (target, gLen);
  341.   IF nLen > 0 THEN
  342.     INC (nLen);
  343.     Block.Copy (ADR(nName), nLen, target);
  344.     gr^.netname := target;
  345.     INC (target, nLen);
  346.   ELSE
  347.     gr^.netname := NIL
  348.   END;
  349.   IF cLen > 0 THEN
  350.     INC (cLen);
  351.     Block.Copy (ADR(cLine), cLen, target);
  352.     gr^.chef := target;
  353.     INC (target, cLen);
  354.   ELSE
  355.     gr^.chef := NIL
  356.   END;
  357.   IF uLen > 0 THEN
  358.     INC (uLen);
  359.     Block.Copy (ADR(uLine), uLen, target);
  360.     gr^.kurztext := target;
  361.     INC (target, uLen);
  362.   ELSE
  363.     gr^.kurztext := NIL
  364.   END;
  365.   IF sLen > 0 THEN
  366.     INC (sLen);
  367.     Block.Copy (ADR(sLine), sLen, target);
  368.     gr^.sysop := target;
  369.     INC (target, sLen);
  370.   ELSE
  371.     gr^.sysop := NIL
  372.   END;
  373.   IF rLen > 0 THEN
  374.     INC (rLen);
  375.     Block.Copy (ADR(rLine), rLen, target);
  376.     gr^.followup := target;
  377.     INC (target, rLen);
  378.   ELSE
  379.     gr^.followup := NIL
  380.   END;
  381.   IF hLen > 0 THEN
  382.     INC (hLen);
  383.     Block.Copy (ADR(hLine), hLen, target);
  384.     gr^.herkunft := target;
  385.     INC (target, hLen);
  386.   ELSE
  387.     gr^.herkunft := NIL
  388.   END;
  389.   IF pLen > 0 THEN
  390.     INC (pLen);
  391.     Block.Copy (ADR(pLine), pLen, target);
  392.     gr^.sprache := target;
  393.     INC (target, pLen);
  394.   ELSE
  395.     gr^.sprache := NIL
  396.   END;
  397.   IF aLen > 0 THEN
  398.     INC (aLen);
  399.     Block.Copy (ADR(aLine), aLen, target);
  400.     gr^.alias := target;
  401.     INC (target, aLen);
  402.   ELSE
  403.     gr^.alias := NIL
  404.   END;
  405.   IF rN IN avail
  406.   THEN
  407.     gr^.readNumber := readNum;
  408.   END;
  409.   IF cN IN avail
  410.   THEN
  411.     gr^.catNumber := catNum
  412.   END;
  413.   IF oN IN avail
  414.   THEN
  415.     gr^.ordered := oState
  416.   END;
  417.   IF dN IN avail
  418.   THEN
  419.     gr^.orderDays := days
  420.   END;
  421.   (* Okay, Texte sind gesetzt, jetzt Flags einsetzen, falls vorhanden *)
  422.   (* Default-Einstellung herstellen fr Flags *)
  423.   IF defaultFlags
  424.   THEN
  425.     gr^.readState := rwNA;
  426.     gr^.writeState := rwNA;
  427.     gr^.userState := uNA;
  428.     gr^.isNet     := nNA;
  429.     gr^.hasProg   := FALSE;
  430.     gr^.withDollar:= FALSE;
  431.     gr^.selected  := FALSE;
  432.   END;
  433.   IF fL IN avail
  434.   THEN
  435.     ParseFlags (gr^, fLine);
  436.   END;
  437.   RETURN TRUE;
  438. END MakeEntry;
  439.  
  440. PROCEDURE UpdateEntry (VAR gName, nName, uLine, cLine, fLine, sLine, rLine, hLine, pLine, aLine : ARRAY OF CHAR;
  441.                        readNum, catNum : INTEGER; oState: orderState; days : INTEGER; 
  442.                        avail : lineSet;
  443.                        keepOld: BOOLEAN;
  444.                        preferLocal: BOOLEAN): BOOLEAN;
  445.  
  446. VAR
  447.     found   : BOOLEAN;
  448.     gr      : groupPtr;
  449.     grIdx   : INTEGER;
  450.     newGroup: group;
  451.     i       : INTEGER;
  452.  
  453. BEGIN
  454.   (* So, und jetzt suchen wir nur nach dem Namen *)
  455.   found := findEntry (gName, grIdx);
  456.   IF found
  457.   THEN
  458.     gr := ADR (groupArray^[grIdx]);
  459.     (* Jetzt das raussuchen, was ggf. neu gesetzt werden muž 
  460.      * Dabei umgedrehte Logik: Wenn etwas nicht in den neu zu setzenden
  461.      * Sachen ist, aber in den alten, dann wird der alte Eintrag einfach
  462.      * fr den neuen bernommen, wenn keepOld TRUE ist 
  463.      * (bei GRUPPEN.INF oder IGK der Fall)
  464.      *)
  465.     IF preferLocal
  466.     THEN
  467.       (* Lokal vorhandene Gruppen bevorzugen *)
  468.       IF gr^.isUpdated
  469.       THEN
  470.         IF (fL IN avail) 
  471.         THEN
  472.           ParseFlags (newGroup, fLine);
  473.           IF (newGroup.isNet = nNetLokal) OR (newGroup.isNet = nNetForbid)
  474.           THEN
  475.             (* Neuer Eintrag ist lokal nicht vorhandene Netzgruppe,
  476.              * wir behalten einfach den alten
  477.              *)
  478.             RETURN TRUE;
  479.           END;
  480.         ELSIF (gr^.isNet # nNetLokal) & (gr^.isNet # nNetForbid) & (gr^.isNet # nNA)
  481.         THEN
  482.           (* Der alte Eintrag bezeichnet schon eine vorhandene Gruppe *)
  483.           RETURN TRUE;
  484.         END;
  485.       END;
  486.     END;
  487.     gr^.isUpdated := TRUE;
  488.     IF keepOld
  489.     THEN
  490.       IF ~(nL IN avail) & (gr^.netname # NIL)
  491.       THEN
  492.         (* Netname zuweisen *)
  493.         MagicStrings.Assign (gr^.netname^, nName);
  494.       END;
  495.       IF ~(uL IN avail) & (gr^.kurztext # NIL)
  496.       THEN
  497.         (* Kurztext zuweisen *)
  498.         MagicStrings.Assign (gr^.kurztext^, uLine);
  499.       END;
  500.       IF ~(cL IN avail) & (gr^.chef # NIL)
  501.       THEN
  502.         (* Chef zuweisen *)
  503.         MagicStrings.Assign (gr^.chef^, cLine);
  504.       END;
  505.       IF ~(sL IN avail) & (gr^.sysop # NIL)
  506.       THEN
  507.         (* Sysopinfo zuweisen *)
  508.         MagicStrings.Assign (gr^.sysop^, sLine);
  509.       END;
  510.     END;
  511.     IF ~(rL IN avail) & (gr^.followup # NIL)
  512.     THEN
  513.       (* Followup zuweisen *)
  514.       MagicStrings.Assign (gr^.followup^, rLine);
  515.     END;
  516.     IF ~(hL IN avail) & (gr^.herkunft # NIL)
  517.     THEN
  518.       (* Herkunft zuweisen *)
  519.       MagicStrings.Assign (gr^.herkunft^, hLine);
  520.     END;
  521.     IF ~(pL IN avail) & (gr^.sprache # NIL)
  522.     THEN
  523.       (* Sprache zuweisen *)
  524.       MagicStrings.Assign (gr^.sprache^, pLine);
  525.     END;
  526.     IF ~(aL IN avail) & (gr^.alias # NIL)
  527.     THEN
  528.       (* Alias zuweisen *)
  529.       MagicStrings.Assign (gr^.alias^, aLine);
  530.     END;
  531.     IF gr^.data # NIL THEN DEALLOCATE (gr^.data, 0) END;
  532.     gr^.dataLength := 0;
  533.   ELSE
  534.     (* Eintrag l”schen *)
  535.     newGroup := group{NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,-1,-1,rwNA,rwNA,uNA,nNA,
  536.                       FALSE,FALSE,FALSE,oNA,0,TRUE,0,NIL};
  537.     gr := ADR (newGroup);
  538.   END;
  539.   IF ~MakeEntry (gr, gName, nName, uLine, cLine, fLine, sLine, rLine, hLine, pLine, aLine, readNum, catNum, oState, days, TRUE, avail)
  540.   THEN 
  541.     RETURN FALSE
  542.   END;
  543.   IF found
  544.   THEN
  545.     (* Ist nichts mehr zu tun, da direkt die Adresse des Eintrags bergeben wurde
  546.      *)
  547.   ELSE
  548.     (* neuen Eintrag anlegen *)
  549.     IF ~MakeNewGroupArray (FALSE, 0)
  550.     THEN RETURN FALSE END;
  551.     (* Alle nachfolgenden verschieben *)
  552.     IF groups > 0
  553.     THEN
  554.       FOR i := groups TO grIdx + 2 BY -1 DO
  555.         groupArray^[i] := groupArray^[i-1]
  556.       END;
  557.     END;
  558.     (* Eintrag bernehmen *)
  559.     groupArray^[grIdx+1] := gr^;
  560.     (* Gruppenanzahl „ndern *)
  561.     INC (groups);
  562.   END;
  563.   (* Eintr„ge jetzt wieder l”schen *)
  564.   MagicStrings.Assign ('', gName);
  565.   MagicStrings.Assign ('', nName);
  566.   MagicStrings.Assign ('', uLine);
  567.   MagicStrings.Assign ('', cLine);
  568.   MagicStrings.Assign ('', fLine);
  569.   MagicStrings.Assign ('', sLine);
  570.   MagicStrings.Assign ('', rLine);
  571.   MagicStrings.Assign ('', hLine);
  572.   MagicStrings.Assign ('', pLine);
  573.   MagicStrings.Assign ('', aLine);
  574.   listModified := TRUE;
  575.   RETURN TRUE;
  576. END UpdateEntry;
  577.  
  578. TYPE    lSortType   = (sByName, sByReadNum, sByNum, sByOrder, sByNet, sByLang);
  579.  
  580. PROCEDURE readNumComp (a1, a2 : ADDRESS) : BOOLEAN;
  581.   VAR p1, p2 : POINTER TO groupPtr;
  582.       str1, str2 : CatTypes.String255;
  583. BEGIN
  584.   p1 := a1;
  585.   p2 := a2;
  586.   IF p1^^.readNumber = p2^^.readNumber
  587.   THEN
  588.     RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  589.     (*
  590.     MagicStrings.Assign (p1^^.name^, str1);
  591.     MagicStrings.Assign (p2^^.name^, str2);
  592.     MagicStrings.CAPS (str1);
  593.     MagicStrings.CAPS (str2);
  594.     RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
  595.     *)
  596.   ELSE 
  597.     RETURN p1^^.readNumber < p2^^.readNumber
  598.   END;
  599. END readNumComp;
  600.  
  601. PROCEDURE orderComp (a1, a2 : ADDRESS) : BOOLEAN;
  602.   VAR p1, p2 : POINTER TO groupPtr;
  603.       str1, str2 : CatTypes.String255;
  604.       on1, on2 : BOOLEAN;
  605. BEGIN
  606.   p1 := a1;
  607.   p2 := a2;
  608.   on1 := (p1^^.readState = rwOn) OR (p1^^.readState = rwDefault);
  609.   on2 := (p2^^.readState = rwOn) OR (p2^^.readState = rwDefault);
  610.   IF on1 = on2
  611.   THEN
  612.     RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  613.     (*
  614.     MagicStrings.Assign (p1^^.name^, str1);
  615.     MagicStrings.Assign (p2^^.name^, str2);
  616.     MagicStrings.CAPS (str1);
  617.     MagicStrings.CAPS (str2);
  618.     RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
  619.     *)
  620.   ELSE 
  621.     RETURN on1
  622.   END;
  623. END orderComp;
  624.  
  625. PROCEDURE nameComp (a1, a2 : ADDRESS) : BOOLEAN;
  626.   VAR p1, p2 : POINTER TO groupPtr;
  627.       str1, str2 : CatTypes.String255;
  628. BEGIN
  629.   p1 := a1;
  630.   p2 := a2;
  631.   RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  632.   (*
  633.   MagicStrings.Assign (p1^^.name^, str1);
  634.   MagicStrings.Assign (p2^^.name^, str2);
  635.   MagicStrings.CAPS (str1);
  636.   MagicStrings.CAPS (str2);
  637.   RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
  638.   *)
  639. END nameComp;
  640.  
  641. PROCEDURE ExtraktString (REF source: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
  642.   VAR p: CARDINAL;
  643. BEGIN
  644.   MagicStrings.Assign ("", dest);
  645.   p := MagicStrings.Pos (":", source, 0, FALSE);
  646.   IF p <= LENGTH (source)
  647.   THEN
  648.     MagicStrings.Copy (source, 0, p, dest);
  649.   END;
  650. END ExtraktString;
  651.  
  652. PROCEDURE netComp (a1, a2 : ADDRESS) : BOOLEAN;
  653.   VAR p1, p2 : POINTER TO groupPtr;
  654.       str1, str2 : CatTypes.String255;
  655.       compRes: MagicStrings.Relation;
  656. BEGIN
  657.   p1 := a1;
  658.   p2 := a2;
  659.   IF (p1^^.herkunft # NIL) & (p2^^.herkunft # NIL)
  660.   THEN
  661.     (* Herkunft extrahieren *)
  662.     ExtraktString (p1^^.herkunft^, str1);
  663.     ExtraktString (p2^^.herkunft^, str2);
  664.     compRes := AssFuncs.StrICompare (str1, str2);
  665.     IF compRes = MagicStrings.equal
  666.     THEN
  667.       RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  668.     END;
  669.     RETURN compRes = MagicStrings.less;
  670.   ELSIF (p1^^.herkunft # NIL)
  671.   THEN
  672.     RETURN TRUE;
  673.   ELSIF (p2^^.herkunft # NIL)
  674.   THEN
  675.     RETURN FALSE;
  676.   END;
  677.   RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  678.   (*
  679.   MagicStrings.Assign (p1^^.name^, str1);
  680.   MagicStrings.Assign (p2^^.name^, str2);
  681.   MagicStrings.CAPS (str1);
  682.   MagicStrings.CAPS (str2);
  683.   RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
  684.   *)
  685. END netComp;
  686.  
  687. PROCEDURE langComp (a1, a2 : ADDRESS) : BOOLEAN;
  688.   VAR p1, p2 : POINTER TO groupPtr;
  689.       str1, str2 : CatTypes.String255;
  690.       compRes: MagicStrings.Relation;
  691. BEGIN
  692.   p1 := a1;
  693.   p2 := a2;
  694.   IF (p1^^.sprache # NIL) & (p2^^.sprache # NIL)
  695.   THEN
  696.     (* sprache extrahieren *)
  697.     ExtraktString (p1^^.sprache^, str1);
  698.     ExtraktString (p2^^.sprache^, str2);
  699.     compRes := AssFuncs.StrICompare (str1, str2);
  700.     IF compRes = MagicStrings.equal
  701.     THEN
  702.       RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  703.     END;
  704.     RETURN compRes = MagicStrings.less;
  705.   ELSIF (p1^^.sprache # NIL)
  706.   THEN
  707.     RETURN TRUE;
  708.   ELSIF (p2^^.sprache # NIL)
  709.   THEN
  710.     RETURN FALSE;
  711.   END;
  712.   RETURN AssFuncs.StrICompare (p1^^.name^, p2^^.name^) = MagicStrings.less;
  713.   (*
  714.   MagicStrings.Assign (p1^^.name^, str1);
  715.   MagicStrings.Assign (p2^^.name^, str2);
  716.   MagicStrings.CAPS (str1);
  717.   MagicStrings.CAPS (str2);
  718.   RETURN MagicStrings.Compare (str1, str2) = MagicStrings.less;
  719.   *)
  720. END langComp;
  721.  
  722. PROCEDURE SortList (VAR l : Lists.List; sortType :lSortType);
  723. (* sortiert eine Gruppenliste *)
  724.  
  725.   VAR count : CARDINAL;
  726.       sort  : POINTER TO ARRAY [0..$FFFF] OF ADDRESS;
  727.       i     : CARDINAL;
  728.       adr   : ADDRESS;
  729.  
  730. BEGIN
  731.   count := Lists.NoOfEntries (l);
  732.   IF count = 0 THEN RETURN END;
  733.   ALLOCATE (sort, LONG(count) * TSIZE (ADDRESS));
  734.   IF sort = NIL THEN RETURN END;
  735.   Lists.ResetList (l);
  736.   FOR i := 0 TO count-1 DO
  737.     sort^[i] := Lists.NextEntry (l);
  738.   END;
  739.   CASE sortType OF
  740.     sByName : v.bool := QuickSort.sortIt (0, count-1, sort^, nameComp, TSIZE (ADDRESS), QuickSort.noBreak); |
  741.     sByReadNum : v.bool := QuickSort.sortIt (0, count-1, sort^, readNumComp, TSIZE (ADDRESS), QuickSort.noBreak); |
  742.     sByOrder : v.bool := QuickSort.sortIt (0, count-1, sort^, orderComp, TSIZE (ADDRESS), QuickSort.noBreak); |
  743.     sByNet   : v.bool := QuickSort.sortIt (0, count-1, sort^, netComp, TSIZE (ADDRESS), QuickSort.noBreak); |
  744.     sByLang  : v.bool := QuickSort.sortIt (0, count-1, sort^, langComp, TSIZE (ADDRESS), QuickSort.noBreak); |
  745.   ELSE
  746.   END;
  747.   (* Liste wieder zurckbernehmen *)
  748.   Lists.ResetList (l);
  749.   FOR i := 0 TO count-1 DO
  750.     adr := Lists.NextEntry (l);
  751.     Lists.RemoveEntry (l, v.bool);
  752.   END;
  753.   Lists.ResetList (l);
  754.   FOR i := 0 TO count - 1 DO 
  755.     Lists.AppendEntry (l, sort^[i], v.bool);
  756.   END;
  757.   DEALLOCATE (sort, 0);
  758. END SortList;
  759.  
  760. PROCEDURE ParseITG(file : mtTextfiles.TEXTFILE);
  761.   VAR gName, nName, uLine, cLine, fLine, 
  762.       sLine, rLine, hLine, aLine, pLine : CatTypes.String255;
  763.       scrap : CatTypes.String255;
  764.       infoWritten: BOOLEAN;
  765.       linesAvail : lineSet;
  766.       idx, i     : INTEGER;
  767.       p0         : CARDINAL;
  768. BEGIN
  769.   (* Erstmal alle Eintr„ge l”schen, die keine CAT-Gruppe sind!
  770.    *)
  771.   idx := 0;
  772.   WHILE idx < groups DO
  773.     WITH groupArray^[idx] DO
  774.       IF catNumber >= 0 THEN
  775.         groupArray^[idx].isUpdated := FALSE; 
  776.         INC (idx)
  777.       ELSE
  778.         FOR i := idx+1 TO groups-1 DO
  779.           (* Eintr„ge runterkopieren *)
  780.           groupArray^[i-1] := groupArray^[i];
  781.         END;
  782.         DEC (groups);
  783.       END;
  784.     END;
  785.   END;
  786.   infoWritten := TRUE;
  787.   linesAvail := lineSet{oN, dN};
  788.   MagicStrings.Assign ('', gName);
  789.   MagicStrings.Assign ('', nName);
  790.   MagicStrings.Assign ('', uLine);
  791.   MagicStrings.Assign ('', cLine);
  792.   MagicStrings.Assign ('', aLine);
  793.   MagicStrings.Assign ('', fLine);
  794.   MagicStrings.Assign ('', sLine);
  795.   MagicStrings.Assign ('', rLine);
  796.   WHILE ~mtTextfiles.EndofText (file) DO
  797.     mtTextfiles.ReadLine (file, scrap);
  798.     mtTextfiles.ReadLn (file);
  799.     CASE scrap[0] OF
  800.       'G' : IF ~infoWritten
  801.             THEN
  802.               v.bool := UpdateEntry (gName, nName, uLine, cLine, fLine, sLine, rLine, hLine, pLine, aLine, -1, -1, oNA, 0, linesAvail, FALSE, TRUE);
  803.               linesAvail := lineSet{oN, dN};
  804.             END;
  805.             MagicStrings.Delete (scrap, 0, 1);
  806.             MagicStrings.Assign (scrap, gName); 
  807.             INCL (linesAvail, gL);
  808.             infoWritten := FALSE; |
  809.       'N' : MagicStrings.Delete (scrap, 0, 1);
  810.             MagicStrings.Assign (scrap, nName); 
  811.             INCL (linesAvail, nL); |
  812.       'U' : MagicStrings.Delete (scrap, 0, 1);
  813.             MagicStrings.Assign (scrap, uLine); 
  814.             INCL (linesAvail, uL); |
  815.       'C' : MagicStrings.Delete (scrap, 0, 1);
  816.             MagicStrings.Assign (scrap, cLine); 
  817.             INCL (linesAvail, cL); |
  818.       'F' : MagicStrings.Delete (scrap, 0, 1);
  819.             MagicStrings.Assign (scrap, fLine); 
  820.             INCL (linesAvail, fL); |
  821.       'S' : MagicStrings.Delete (scrap, 0, 1);
  822.             MagicStrings.Assign (scrap, sLine); 
  823.             INCL (linesAvail, sL); |
  824.       'R' : MagicStrings.Delete (scrap, 0, 1);
  825.             MagicStrings.Assign (scrap, rLine); 
  826.             INCL (linesAvail, rL); |
  827.       'H' : MagicStrings.Delete (scrap, 0, 1);
  828.             MagicStrings.Assign (scrap, hLine); 
  829.             INCL (linesAvail, hL); |
  830.       'P' : MagicStrings.Delete (scrap, 0, 1);
  831.             MagicStrings.Assign (scrap, pLine); 
  832.             INCL (linesAvail, pL); |
  833.       'A' : MagicStrings.Delete (scrap, 0, 1);
  834.             MagicStrings.Assign (scrap, aLine); 
  835.             INCL (linesAvail, aL); |
  836.     ELSE
  837.     END;
  838.   END;
  839.   IF ~infoWritten 
  840.   THEN
  841.     v.bool := UpdateEntry (gName, nName, uLine, cLine, fLine, sLine, rLine, hLine, pLine, aLine, -1, -1, oNA, 0, linesAvail, FALSE, TRUE);
  842.   END;
  843. END ParseITG;
  844.  
  845. PROCEDURE ParseIGK (file: mtTextfiles.TEXTFILE);
  846.   VAR gName, uLine, fLine : CatTypes.String255;
  847.       scrap : CatTypes.String255;
  848.       linesAvail : lineSet;
  849.       p     : CARDINAL;
  850.  
  851.   PROCEDURE isGroupName (REF s : ARRAY OF CHAR) : BOOLEAN;
  852.     VAR valid : BOOLEAN;
  853.   BEGIN
  854.     (* 1. Zeichen: Darf Space, + oder - sein. *)
  855.     valid := (s[0] = '+') OR (s[0] = '-') OR (s[0] = ' ');
  856.     (* 2. Zeichen: Darf '*' oder Space sein *)
  857.     valid := valid & ((s[1] = ' ') OR (s[1] = '*'));
  858.     (* 3. Zeichen: Darf nicht Space sein! *)
  859.     valid := valid & ~(s[2] = ' ');
  860.     RETURN valid;
  861.   END isGroupName;
  862.  
  863. BEGIN
  864.   linesAvail := lineSet{gL, uL, fL, oN, dN};
  865.   MagicStrings.Assign ('', gName);
  866.   MagicStrings.Assign ('', uLine);
  867.   MagicStrings.Assign ('', fLine);
  868.   WHILE ~mtTextfiles.EndofText (file) DO
  869.     mtTextfiles.ReadLine (file, scrap);
  870.     mtTextfiles.ReadLn(file);
  871.     IF isGroupName (scrap)
  872.     THEN
  873.       IF scrap[0] = '+'
  874.       THEN
  875.         MagicStrings.Append ('L+S+', fLine);
  876.       ELSIF scrap[0] = '-'
  877.       THEN
  878.         MagicStrings.Append ('LBSB', fLine);
  879.       END;
  880.       IF scrap[1] = '*'
  881.       THEN
  882.         MagicStrings.Append ('G*', fLine);
  883.       ELSIF scrap[0] = '-'
  884.       THEN
  885.         MagicStrings.Append ('G+', fLine);
  886.       END;
  887.       (* Flagzeile ist zusammengebaut *)
  888.       IF scrap[2] = '?'
  889.       THEN
  890.         (* Fragezeichen entfernen *)
  891.         MagicStrings.Delete (scrap, 2, 1);
  892.       END;
  893.       (* Jetzt Gruppennamen rauskopieren. Gruppenname geht bis zum ersten Space
  894.        * ab der 3. Spalte, aber mindestens 10 Zeichen weit!
  895.        *)
  896.       p := MagicStrings.Pos (' ', scrap, 2, FALSE);
  897.       IF (p < (*SYSTEM.*)LENGTH (scrap)) 
  898.       THEN
  899.         (* Space gefunden *)
  900.         IF p < 13
  901.         THEN
  902.           (* Mindestens bis zur 13 Stelle kopieren und dann Spaces 
  903.            * am Ende abschneiden 
  904.            *)
  905.           (* Erstmal nachsehen, ob nach dem Space noch ein Zeichen kommt
  906.            *)
  907.           IF scrap[p+1] # ' '
  908.           THEN
  909.             p := MagicStrings.Pos (' ', scrap, p+1, FALSE);
  910.             IF (p < (*SYSTEM.*)LENGTH (scrap)) 
  911.             THEN
  912.               (* Space gefunden *)
  913.               IF p < 13
  914.               THEN
  915.                 p := 13;
  916.               END;
  917.             END;
  918.           ELSE
  919.             p := 13;
  920.           END;
  921.         END;
  922.       ELSE
  923.         p := 13;
  924.       END;
  925.       (* Gruppenname kopieren *)
  926.       MagicStrings.Copy (scrap, 2, p-2, gName);
  927.       (* Rest ist Beschreibung *)
  928.       MagicStrings.Delete (scrap, 0, p);
  929.       MagicStrings.Assign (scrap, uLine);
  930.       (* Jetzt Spaces l”schen *)
  931.       Strings.DelTrailingBlanks (gName);
  932.       Strings.DelTrailingBlanks (uLine);
  933.       Strings.DelLeadingBlanks (gName);
  934.       Strings.DelLeadingBlanks (uLine);
  935.       (* Und jetzt Eintrag updaten *)
  936.       MagicStrings.Assign ('', scrap);
  937.       v.bool := UpdateEntry (gName, scrap, uLine, scrap, fLine, scrap, scrap, scrap, scrap, scrap, -1, -1, oNA, 0, linesAvail, TRUE, FALSE);
  938.     END;
  939.   END;  
  940. END ParseIGK;
  941.  
  942. PROCEDURE compGrName (a1, a2 : ADDRESS) : BOOLEAN;
  943.   VAR p1, p2 : groupPtr;
  944.       str1, str2 : CatTypes.String255;
  945. BEGIN
  946.   p1 := a1;
  947.   p2 := a2;
  948.   (*
  949.   IF p1^.name = NIL THEN RETURN TRUE END;
  950.   IF p2^.name = NIL THEN RETURN FALSE END;
  951.   *)
  952.   MagicStrings.Assign (p1^.name^, str1);
  953.   MagicStrings.Assign (p2^.name^, str2);
  954.   RETURN AssFuncs.StrICompare (str1, str2) = MagicStrings.less;
  955. END compGrName;
  956.  
  957. PROCEDURE SortGroupArray();
  958. BEGIN
  959.   v.bool := QuickSort.sortIt (0, groups-1, groupArray^, compGrName, TSIZE (group), QuickSort.noBreak);
  960. END SortGroupArray;
  961.  
  962. PROCEDURE ParseRenamefile ();
  963.   VAR 
  964.       fname     : CatTypes.String255;
  965.       file      : mtTextfiles.TEXTFILE;
  966.       first,
  967.       success,
  968.       newSet,
  969.       oldSet    : BOOLEAN;
  970.       scrap,
  971.       newName,
  972.       oldName   : CatTypes.String255;
  973. BEGIN
  974.   first  := TRUE;
  975.   newSet := FALSE;
  976.   oldSet := FALSE;
  977.   MagicStrings.Assign (DataPath, fname);
  978.   MagicStrings.Append (renameFileName, fname);
  979.   IF mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 16384, file)
  980.   THEN
  981.     WHILE ~mtTextfiles.EndofText (file) DO
  982.       mtTextfiles.ReadLine (file, scrap);
  983.       mtTextfiles.ReadLn (file);
  984.       CASE scrap[0] OF
  985.         'O': MagicStrings.Delete (scrap, 0, 1);
  986.              MagicStrings.Assign (scrap, oldName); 
  987.              oldSet := TRUE; |
  988.         'N': MagicStrings.Delete (scrap, 0, 1);
  989.              MagicStrings.Assign (scrap, newName); 
  990.              newSet := TRUE; |
  991.       ELSE
  992.       END (* CASE *);
  993.       IF newSet & oldSet
  994.       THEN
  995.         success := RenameGroup (oldName, newName, FALSE);
  996.         IF first 
  997.         THEN
  998.           CatLog.WriteLn ();
  999.           CatLog.WriteLn ();
  1000.           first := FALSE;
  1001.         END;
  1002.         IF success
  1003.         THEN
  1004.           CatLog.putTime ();
  1005.           CatLog.WriteString ("Gruppe ");
  1006.           CatLog.WriteLine (oldName);
  1007.           CatLog.WriteString ("         umbenannt nach ");
  1008.           CatLog.WriteLine (newName);
  1009.         ELSE
  1010.           CatLog.putTime ();
  1011.           CatLog.WriteString ("FEHLER! Gruppe ");
  1012.           CatLog.WriteLine (oldName);
  1013.           CatLog.WriteString ("         konnte nicht umbenannt werden nach ");
  1014.           CatLog.WriteLine (newName);
  1015.         END;
  1016.         oldSet := FALSE;
  1017.         newSet := FALSE;
  1018.       END;
  1019.     END; (* WHILE *)
  1020.     mtTextfiles.CloseTextfile (file);
  1021.   END;
  1022. END ParseRenamefile;
  1023.  
  1024. PROCEDURE ParseInfofile (): BOOLEAN;
  1025.   VAR parseItg  : BOOLEAN;
  1026.       fname     : CatTypes.String255;
  1027.       file      : mtTextfiles.TEXTFILE;
  1028. BEGIN
  1029.   parseItg := Infofiles.GetInfoFilename ('ITG',fname);
  1030.   IF parseItg
  1031.   THEN
  1032.     IF ~mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 32767, file)
  1033.     THEN
  1034.       parseItg := FALSE;
  1035.     ELSE
  1036.       ParseITG (file);
  1037.       mtTextfiles.CloseTextfile (file);
  1038.       (* Die Liste muž nicht mehr sortiert werden, da die automatisch sortiert
  1039.        * eingefgt werden
  1040.        *)
  1041.     END;
  1042.   END;
  1043.   IF ~parseItg
  1044.   THEN
  1045.     (* mit IGK versuchen *)
  1046.     IF Infofiles.GetInfoFilename ('IGK', fname)
  1047.      & mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 32767, file)
  1048.     THEN
  1049.       ParseIGK (file);
  1050.       mtTextfiles.CloseTextfile (file);
  1051.       (* Die Liste muž nicht mehr sortiert werden, da die automatisch sortiert
  1052.        * eingefgt werden
  1053.        *)
  1054.     ELSE
  1055.       RETURN FALSE
  1056.     END;
  1057.   END;
  1058.   RETURN TRUE;
  1059. END ParseInfofile;
  1060.  
  1061. PROCEDURE CountInfofileLines (): CARDINAL;
  1062.   VAR parseItg  : BOOLEAN;
  1063.       fname     : CatTypes.String255;
  1064.       file      : mtTextfiles.TEXTFILE;
  1065.       lines     : CARDINAL;
  1066.       ch        : CHAR;
  1067. BEGIN
  1068.   lines := 0;
  1069.   parseItg := Infofiles.GetInfoFilename ('ITG',fname);
  1070.   IF parseItg
  1071.   THEN
  1072.     IF ~mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 8192, file)
  1073.     THEN
  1074.       parseItg := FALSE;
  1075.     ELSE
  1076.       WHILE ~mtTextfiles.EndofText (file) DO
  1077.         mtTextfiles.ReadChar (file, ch);
  1078.         mtTextfiles.ReadLn (file);
  1079.         IF (ch = 'G')
  1080.         THEN
  1081.           (* Nur Zeilen mit Gruppennamen z„hlen bei der ITG *)
  1082.           INC (lines);
  1083.         END;
  1084.       END;
  1085.       mtTextfiles.CloseTextfile (file);
  1086.     END;
  1087.   END;
  1088.   IF ~parseItg
  1089.   THEN
  1090.     (* mit IGK versuchen *)
  1091.     IF Infofiles.GetInfoFilename ('IGK', fname)
  1092.      & mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 8192, file)
  1093.     THEN
  1094.       WHILE ~mtTextfiles.EndofText (file) DO
  1095.         mtTextfiles.ReadLn (file);
  1096.         INC (lines);
  1097.       END;
  1098.       mtTextfiles.CloseTextfile (file);
  1099.     END;
  1100.   END;
  1101.   RETURN lines;
  1102. END CountInfofileLines;
  1103.       
  1104. CONST   catGroupInf = 'gruppen.inf';
  1105.  
  1106. PROCEDURE ParseGruppenInf(file : mtTextfiles.TEXTFILE): BOOLEAN;
  1107.   VAR gName, void : CatTypes.String255;
  1108.       linesAvail : lineSet;
  1109.       lineNum    : INTEGER;
  1110.       i          : INTEGER;
  1111.       gr         : groupPtr;
  1112. BEGIN
  1113.   linesAvail := lineSet{gL, cN, rN, oN, dN};
  1114.   MagicStrings.Assign ('', gName);
  1115.   MagicStrings.Assign ('', void);
  1116.   lineNum := 1;
  1117.   WHILE ~mtTextfiles.EndofText (file) DO
  1118.     mtTextfiles.ReadLine (file, gName);
  1119.     mtTextfiles.ReadLn(file);
  1120.     IF gName[0] # ''
  1121.     THEN
  1122.       IF ~UpdateEntry (gName, void, void, void, void, void, void, void, void, void, lineNum, lineNum, oNA, 0, linesAvail, TRUE, FALSE)
  1123.       THEN
  1124.         RETURN FALSE
  1125.       END;
  1126.       INC (lineNum);
  1127.     END;
  1128.   END;
  1129.   ConfVars.GetConfDefBool (cSortGroups, v.bool, TRUE);
  1130.   IF v.bool
  1131.   THEN
  1132.     (* Einfach die Lesenummern neu vergeben, da die Liste schon beim
  1133.      * erstellen sortiert wird
  1134.      *)
  1135.     lineNum := 1;
  1136.     FOR i := 0 TO groups - 1 DO
  1137.       IF groupArray^[i].catNumber >= 0 THEN 
  1138.         groupArray^[i].readNumber := lineNum;
  1139.         INC (lineNum);
  1140.       END;
  1141.     END;
  1142.   END; 
  1143.   RETURN TRUE;
  1144. END ParseGruppenInf;
  1145.  
  1146. (*----- Eigene Liste schreiben und lesen ------------------------------*)
  1147.  
  1148. (* Format der Datei, in der die Liste abgespeichert wird: 
  1149.  * Pro Eintrag eine Zeile, die einzelnen Elemente sind getrennt durch ein 
  1150.  * TAB-Zeichen (das kommt definitiv nie in Gruppennamen vor).
  1151.  * <name>TAB<netname>TAB<chef>TAB<kurztext>TAB<sysopinfo>TAB<followupto>TAB<readNumber>TAB<catNumber>TAB<flags>
  1152.  * Dabei werden die Flags wie im ITG gespeichert.
  1153.  *)
  1154.  
  1155. PROCEDURE SaveGroupList(): BOOLEAN;
  1156.   VAR fname : CatTypes.String255;
  1157.       file  : mtTextfiles.TEXTFILE;
  1158.       i     : INTEGER;
  1159.       dbHdr : dataSys.FileHeaderType;
  1160.       fhdl  : INTEGER;
  1161.   
  1162.   PROCEDURE saveEntry (e, i : ADDRESS);
  1163.     TYPE  shortStr   = ARRAY [0..7] OF CHAR;
  1164.     CONST rwStateStr = shortStr(' +BCS-P ');
  1165.           uStateStr  = shortStr(' *+-    ');
  1166.           nStateStr  = shortStr(' +-=!   ');
  1167.           TAB        = 11C;
  1168.     VAR flags : CatTypes.String127;
  1169.         gr    : groupPtr;
  1170.   BEGIN
  1171.     gr := groupPtr (e);
  1172.     WITH gr^ DO
  1173.       IF name # NIL THEN mtTextfiles.WriteLine (file, name^); END;
  1174.       mtTextfiles.WriteChar (file, TAB);
  1175.       IF netname # NIL THEN mtTextfiles.WriteLine (file, netname^); END;
  1176.       mtTextfiles.WriteChar (file, TAB);
  1177.       IF chef # NIL THEN mtTextfiles.WriteLine (file, chef^); END;
  1178.       mtTextfiles.WriteChar (file, TAB);
  1179.       IF kurztext # NIL THEN mtTextfiles.WriteLine (file, kurztext^); END;
  1180.       mtTextfiles.WriteChar (file, TAB);
  1181.       IF sysop # NIL THEN mtTextfiles.WriteLine (file, sysop^); END;
  1182.       mtTextfiles.WriteChar (file, TAB);
  1183.       IF followup # NIL THEN mtTextfiles.WriteLine (file, followup^); END;
  1184.       mtTextfiles.WriteChar (file, TAB);
  1185.       mtTextfiles.WriteInt (file, readNumber, 0);
  1186.       mtTextfiles.WriteChar (file, TAB);
  1187.       mtTextfiles.WriteInt (file, catNumber, 0);
  1188.       mtTextfiles.WriteChar (file, TAB);
  1189.       MagicStrings.Assign ('', flags);
  1190.       IF readState # rwNA THEN 
  1191.         MagicStrings.Append ('L', flags); 
  1192.         MagicStrings.Append (rwStateStr[ORD(readState)], flags);
  1193.       END;
  1194.       IF writeState # rwNA THEN 
  1195.         MagicStrings.Append ('S', flags); 
  1196.         MagicStrings.Append (rwStateStr[ORD(writeState)], flags);
  1197.       END;
  1198.       IF userState # uNA THEN 
  1199.         MagicStrings.Append ('G', flags); 
  1200.         MagicStrings.Append (uStateStr[ORD(userState)], flags);
  1201.       END;
  1202.       IF isNet # nNA THEN 
  1203.         MagicStrings.Append ('V', flags); 
  1204.         MagicStrings.Append (nStateStr[ORD(isNet)], flags);
  1205.       END;
  1206.       IF hasProg THEN 
  1207.         MagicStrings.Append ('P+', flags);
  1208.       ELSE
  1209.         MagicStrings.Append ('P-', flags);
  1210.       END;
  1211.       IF withDollar THEN 
  1212.         MagicStrings.Append ('$+', flags);
  1213.       ELSE
  1214.         MagicStrings.Append ('$-', flags);
  1215.       END;
  1216.       mtTextfiles.WriteLine (file, flags);
  1217.       mtTextfiles.WriteChar (file, TAB);
  1218.       mtTextfiles.WriteInt (file, ORD(ordered), 0);
  1219.       mtTextfiles.WriteChar (file, TAB);
  1220.       mtTextfiles.WriteInt (file, orderDays, 0);
  1221.       mtTextfiles.WriteChar (file, TAB);
  1222.       IF herkunft # NIL THEN mtTextfiles.WriteLine (file, herkunft^); END;
  1223.       mtTextfiles.WriteChar (file, TAB);
  1224.       IF sprache  # NIL THEN mtTextfiles.WriteLine (file, sprache^); END;
  1225.       mtTextfiles.WriteChar (file, TAB);
  1226.       IF alias    # NIL THEN mtTextfiles.WriteLine (file, alias^); END;
  1227.       mtTextfiles.WriteLn(file);
  1228.     END;
  1229.   END saveEntry;
  1230.   
  1231.   PROCEDURE saveDataEntry (e, i : ADDRESS): BOOLEAN;
  1232.     VAR gr    : groupPtr;
  1233.         hdr   : groupHdr;
  1234.         fPtr  : POINTER TO INTEGER;
  1235.   BEGIN
  1236.     gr := groupPtr (e);
  1237.     fPtr := i;
  1238.     WITH hdr DO
  1239.       name := 0;
  1240.       netname       := 0;
  1241.       chef          := 0;
  1242.       kurztext      := 0;
  1243.       sysop         := 0;
  1244.       followup      := 0;
  1245.       herkunft      := 0;
  1246.       sprache       := 0;
  1247.       alias         := 0;
  1248.       IF gr^.netname  # NIL THEN netname  := SHORT(LONGCARD(gr^.netname - gr^.data)) ; END;
  1249.       IF gr^.chef     # NIL THEN chef     := SHORT(LONGCARD(gr^.chef - gr^.data)); END;
  1250.       IF gr^.kurztext # NIL THEN kurztext := SHORT(LONGCARD(gr^.kurztext - gr^.data)); END;
  1251.       IF gr^.sysop    # NIL THEN sysop    := SHORT(LONGCARD(gr^.sysop - gr^.data)); END;
  1252.       IF gr^.followup # NIL THEN followup := SHORT(LONGCARD(gr^.followup - gr^.data)); END;
  1253.       IF gr^.herkunft # NIL THEN herkunft := SHORT(LONGCARD(gr^.herkunft - gr^.data)); END;
  1254.       IF gr^.sprache  # NIL THEN sprache  := SHORT(LONGCARD(gr^.sprache  - gr^.data)); END;
  1255.       IF gr^.alias    # NIL THEN alias    := SHORT(LONGCARD(gr^.alias    - gr^.data)); END;
  1256.       (* Jetzt die brigen Daten setzen *)
  1257.       readNumber := gr^.readNumber;
  1258.       catNumber  := gr^.catNumber ;
  1259.       readState  := gr^.readState ;   (* F-Zeile *)
  1260.       writeState := gr^.writeState;   (* F-Zeile *)
  1261.       userState  := gr^.userState ;   (* F-Zeile *)
  1262.       isNet      := gr^.isNet     ;   (* F-Zeile *)
  1263.       hasProg    := gr^.hasProg   ;   (* F-Zeile *)
  1264.       withDollar := gr^.withDollar;   (* F-Zeile *)
  1265.       selected   := gr^.selected  ;   (* Fr Darstellung in Liste, wird beim Einlesen auf FALSE gesetzt *)
  1266.       ordered    := gr^.ordered   ;   (* Fr An-/Abbestellen von Gruppen *)
  1267.       orderDays  := gr^.orderDays ;   (* Madness-Erweiterung: Gruppe soviel Tage zurck bestellen *)
  1268.       dataLength := gr^.dataLength;   (* Die L„nge des folgenden Datenblocks *)
  1269.     END;
  1270.     CatFiles.WriteMuch (TSIZE (groupHdr), fPtr^, ADR(hdr));
  1271.     IF CatFiles.FileError # CatFiles.NoError 
  1272.     THEN
  1273.       RETURN FALSE
  1274.     END;
  1275.     (* Jetzt die Daten schreiben *)
  1276.     CatFiles.WriteMuch (hdr.dataLength, fPtr^, gr^.data);
  1277.     RETURN CatFiles.FileError = CatFiles.NoError;
  1278.   END saveDataEntry;
  1279.   
  1280. BEGIN
  1281.   MagicStrings.Assign (DataPath, fname);
  1282.   MagicStrings.Append (groupFileName, fname);
  1283.   IF mtTextfiles.OpenTextfile (fname, mtTextfiles.WRITE, 16384, file)
  1284.   THEN
  1285.     FOR i := 0 TO groups - 1 DO
  1286.       saveEntry (ADR(groupArray^[i]), ADR(file));
  1287.     END;
  1288.     mtTextfiles.CloseTextfile (file);
  1289.     listModified := FALSE;
  1290.     (* Jetzt noch das Bin„rfile schreiben *)
  1291.     fhdl := CatFiles.CreateFile (DataPath, groupDataName);
  1292.     IF fhdl > 0
  1293.     THEN
  1294.       (* Header schreiben *)
  1295.       dbHdr.CatMagic := groupCatMagic;
  1296.       dbHdr.Version  := groupVersion;
  1297.       dbHdr.VersionMagic := groupVersionMagic;
  1298.       CatFiles.WriteMuch (dataSys.dbHeaderLength, fhdl, ADR(dbHdr));
  1299.       IF CatFiles.FileError # CatFiles.NoError
  1300.       THEN
  1301.         CatFiles.CloseFile (fhdl);
  1302.         RETURN TRUE;
  1303.       END;
  1304.       i := 0;
  1305.       WHILE (i < groups) & 
  1306.             saveDataEntry (ADR(groupArray^[i]), ADR(fhdl)) DO INC (i) END;
  1307.       CatFiles.CloseFile (fhdl);
  1308.     END;
  1309.     Protokoll.SendPathUpdate (DataPath);
  1310.     RETURN TRUE;
  1311.   ELSE
  1312.     (* Fehlermeldung rauswerfen *)
  1313.     MTE.InfoAlert (MTE.noFile1, groupFileName, MTE.noFile4);
  1314.     RETURN FALSE;
  1315.   END;
  1316. END SaveGroupList;
  1317.  
  1318. PROCEDURE SaveChangedGroupList(): BOOLEAN;
  1319. (* Sichert die interne Gruppenliste in die Datei CATGROUP.INF, 
  1320.  * wenn sie seit dem letzten Einlesen irgendwie ge„ndert wurde
  1321.  *)
  1322. BEGIN
  1323.   IF listModified THEN RETURN SaveGroupList(); END;
  1324.   RETURN TRUE
  1325. END SaveChangedGroupList;
  1326.  
  1327. PROCEDURE GetString (VAR str : ARRAY OF CHAR; p0 : CARDINAL;
  1328.                      VAR str2 : ARRAY OF CHAR; VAR p1 : CARDINAL);
  1329.   CONST TAB        = 11C;
  1330. BEGIN
  1331.   p1 := Strings.PosLen (TAB, str, p0);
  1332.   IF p1 > p0
  1333.   THEN
  1334.     Strings.Copy (str, p0, p1-p0, str2, v.bool);
  1335.   ELSE
  1336.     Strings.Assign ("", str2, v.bool);
  1337.   END;
  1338. END GetString;
  1339.  
  1340. PROCEDURE ReadGroupList(): BOOLEAN;
  1341.   VAR fname : CatTypes.String255;
  1342.       file  : mtTextfiles.TEXTFILE;
  1343.       fhdl  : INTEGER;
  1344.       scrap : CatTypes.String255;
  1345.       lc    : CARDINAL;
  1346.  
  1347.   VAR  
  1348.       p0, p1  : CARDINAL;
  1349.       ordDays,
  1350.       catNum,
  1351.       readNum : INTEGER;
  1352.       oState  : orderState;
  1353.       avail   : lineSet;
  1354.       fLine,
  1355.       oLine,
  1356.       mNumber,
  1357.       oNumber,
  1358.       rNumber,
  1359.       cNumber : ARRAY [0..30] OF CHAR;
  1360.       gName, nName, uLine, cLine, sLine, rLine, hLine, pLine, aLine : CatTypes.String255;
  1361.   
  1362.   PROCEDURE parseLines (): BOOLEAN;
  1363.     VAR gr : groupPtr;
  1364.         lines : CARDINAL;
  1365.   BEGIN
  1366.     (* Erstmal Zeilen z„hlen *)
  1367.     lines := 0;
  1368.     WHILE ~mtTextfiles.EndofText (file) DO
  1369.       mtTextfiles.ReadLn (file);
  1370.       INC (lines);
  1371.     END;
  1372.     (* Jetzt wieder an den Anfang gehen *)
  1373.     mtTextfiles.Reset (file);
  1374.     (* Jetzt allozieren *)
  1375.     IF ~MakeNewGroupArray (TRUE, lines)
  1376.     THEN 
  1377.       RETURN FALSE
  1378.     END;
  1379.     lines := 0;
  1380.     WHILE ~mtTextfiles.EndofText (file) DO
  1381.       p0 := 0;
  1382.       p1 := 0;
  1383.       (* Eine Zeile lesen und parsen *)
  1384.       mtTextfiles.ReadLine (file, scrap);
  1385.       mtTextfiles.ReadLn(file);
  1386.       GetString (scrap, p0, gName, p1);
  1387.       p0 := p1+1;
  1388.       GetString (scrap, p0, nName, p1);
  1389.       p0 := p1+1;
  1390.       GetString (scrap, p0, cLine, p1);
  1391.       p0 := p1+1;
  1392.       GetString (scrap, p0, uLine, p1);
  1393.       p0 := p1+1;
  1394.       GetString (scrap, p0, sLine, p1);
  1395.       p0 := p1+1;
  1396.       GetString (scrap, p0, rLine, p1);
  1397.       p0 := p1+1;
  1398.       GetString (scrap, p0, rNumber, p1);
  1399.       p0 := p1+1;
  1400.       GetString (scrap, p0, cNumber, p1);
  1401.       p0 := p1+1;
  1402.       GetString (scrap, p0, fLine, p1);
  1403.       p0 := p1+1;
  1404.       GetString (scrap, p0, oLine, p1);
  1405.       p0 := p1+1;
  1406.       GetString (scrap, p0, oNumber, p1);
  1407.       p0 := p1+1;
  1408.       GetString (scrap, p0, hLine, p1);
  1409.       p0 := p1+1;
  1410.       GetString (scrap, p0, pLine, p1);
  1411.       p0 := p1+1;
  1412.       GetString (scrap, p0, aLine, p1);
  1413.       avail := lineSet {gL..dN};
  1414.       (* Orderstatus herausfinden *)
  1415.       IF oLine[0] # ''
  1416.       THEN
  1417.         p0 := 0;
  1418.         catNum := StrConv.StrToInt (oLine, p0, v.bool);
  1419.         oState := orderState (catNum);
  1420.       ELSE
  1421.         oState := oNA;
  1422.       END;
  1423.       IF oNumber[0] # ''
  1424.       THEN
  1425.         p0 := 0;
  1426.         ordDays := StrConv.StrToInt (oNumber, p0, v.bool);
  1427.       ELSE
  1428.         ordDays := 0;
  1429.       END;
  1430.       (* Jetzt die Lesenummern rausfinden *)
  1431.       IF cNumber[0] # ''
  1432.       THEN
  1433.         p0 := 0;
  1434.         catNum := StrConv.StrToInt (cNumber, p0, v.bool);
  1435.       ELSE
  1436.         catNum := -1;
  1437.       END;
  1438.       IF rNumber[0] # ''
  1439.       THEN
  1440.         p0 := 0;
  1441.         readNum := StrConv.StrToInt (rNumber, p0, v.bool);
  1442.       ELSE
  1443.         readNum := -1;
  1444.       END;
  1445.       (* Jetzt Eintrag setzen *)
  1446.       gr := ADR (groupArray^[lines]);
  1447.       gr^.data := NIL;
  1448.       gr^.dataLength := 0;
  1449.       IF ~MakeEntry (gr, gName, nName, uLine, cLine, fLine, sLine, rLine, hLine, pLine, aLine, readNum, catNum, oState, ordDays, TRUE, avail)
  1450.       THEN
  1451.         RETURN FALSE
  1452.       END;
  1453.       (* Ok, Eintrag jetzt vollst„ndig. Jetzt anh„ngen *)
  1454.       (* Ist durch Array schon angeh„ngt :-) *)
  1455.       INC (lines);
  1456.       INC (groups);
  1457.     END;
  1458.     RETURN TRUE;
  1459.   END parseLines;
  1460.   
  1461.   PROCEDURE parseData(): BOOLEAN;
  1462.     VAR dbHdr   : dataSys.FileHeaderType;
  1463.         hdr     : groupHdr;
  1464.         gr      : groupPtr;
  1465.   BEGIN
  1466.     (* Aufbau der CATGROUP.DAT: 
  1467.      * fileHeader
  1468.      * pro Gruppe:
  1469.      * Header
  1470.      * Textdaten (variabel)
  1471.      *)
  1472.     CatFiles.ReadMuch (dataSys.dbHeaderLength, fhdl, ADR(dbHdr));
  1473.     IF CatFiles.FileError # CatFiles.NoError
  1474.     THEN
  1475.       RETURN FALSE
  1476.     END;
  1477.     (* Jetzt berprfen des Headers *)
  1478.     IF (dbHdr.CatMagic # groupCatMagic)
  1479.     OR (dbHdr.Version  # groupVersion)
  1480.     OR (dbHdr.VersionMagic # groupVersionMagic)
  1481.     THEN
  1482.       (* Abbrechen *)
  1483.       RETURN FALSE
  1484.     END;
  1485.     (* hier eventuell mal z„hlen, wie viele es denn sind *)
  1486.     IF ~MakeNewGroupArray (TRUE, 600)
  1487.     THEN 
  1488.       RETURN FALSE
  1489.     END;
  1490.     groups := 0;
  1491.     REPEAT
  1492.       (* Header lesen *)
  1493.       CatFiles.ReadMuch (TSIZE (groupHdr), fhdl, ADR(hdr));
  1494.       IF CatFiles.FileError = CatFiles.NoError
  1495.       THEN
  1496.         (* Ok, Header gelesen *)
  1497.         IF ~MakeNewGroupArray (FALSE, 0)
  1498.         THEN
  1499.           RETURN FALSE
  1500.         END;
  1501.         (* Jetzt Eintrag setzen *)
  1502.         gr := ADR (groupArray^[groups]);
  1503.         ALLOCATE (gr^.data, hdr.dataLength);
  1504.         IF gr^.data = NIL THEN RETURN FALSE END;
  1505.         (* Jetzt Daten kopieren *)
  1506.         WITH gr^ DO
  1507.           (* Pointer setzen *)
  1508.           name := data;
  1509.           netname       := NIL;
  1510.           chef          := NIL;
  1511.           kurztext      := NIL;
  1512.           sysop         := NIL;
  1513.           followup      := NIL;
  1514.           herkunft      := NIL;
  1515.           sprache       := NIL;
  1516.           alias         := NIL;
  1517.           IF hdr.netname  # 0 THEN netname  := data + ADDRESS(LONG(hdr.netname)); END;
  1518.           IF hdr.chef     # 0 THEN chef     := data + ADDRESS(LONG(hdr.chef)); END;
  1519.           IF hdr.kurztext # 0 THEN kurztext := data + ADDRESS(LONG(hdr.kurztext)); END;
  1520.           IF hdr.sysop    # 0 THEN sysop    := data + ADDRESS(LONG(hdr.sysop)); END;
  1521.           IF hdr.followup # 0 THEN followup := data + ADDRESS(LONG(hdr.followup)); END;
  1522.           IF hdr.herkunft # 0 THEN herkunft := data + ADDRESS(LONG(hdr.herkunft)); END;
  1523.           IF hdr.sprache  # 0 THEN sprache  := data + ADDRESS(LONG(hdr.sprache)); END;
  1524.           IF hdr.alias    # 0 THEN alias    := data + ADDRESS(LONG(hdr.alias)); END;
  1525.           (* Jetzt die brigen Daten setzen *)
  1526.           readNumber := hdr.readNumber;
  1527.           catNumber  := hdr.catNumber ;
  1528.           readState  := hdr.readState ;   (* F-Zeile *)
  1529.           writeState := hdr.writeState;   (* F-Zeile *)
  1530.           userState  := hdr.userState ;   (* F-Zeile *)
  1531.           isNet      := hdr.isNet     ;   (* F-Zeile *)
  1532.           hasProg    := hdr.hasProg   ;   (* F-Zeile *)
  1533.           withDollar := hdr.withDollar;   (* F-Zeile *)
  1534.           selected   := FALSE  ;          (* Fr Darstellung in Liste, wird beim Einlesen auf FALSE gesetzt *)
  1535.           ordered    := hdr.ordered   ;   (* Fr An-/Abbestellen von Gruppen *)
  1536.           orderDays  := hdr.orderDays ;   (* Madness-Erweiterung: Gruppe soviel Tage zurck bestellen *)
  1537.           dataLength := hdr.dataLength;   (* Die L„nge des folgenden Datenblocks *)
  1538.           isUpdated  := FALSE;            (* Eintrag wurde noch nicht upgedatet *)
  1539.         END;
  1540.         (* Jetzt Daten lesen *)
  1541.         CatFiles.ReadMuch (hdr.dataLength, fhdl, gr^.data);
  1542.         IF CatFiles.FileError # CatFiles.NoError
  1543.         THEN
  1544.           (* Lesefehler, abbrechen *)
  1545.           RETURN FALSE
  1546.         END;
  1547.         INC (groups);
  1548.       END;
  1549.     UNTIL CatFiles.FileError # CatFiles.NoError;
  1550.     RETURN TRUE;
  1551.   END parseData;
  1552.  
  1553.   VAR size  : LONGCARD;
  1554.       exists: BOOLEAN;
  1555.       
  1556.       i     : INTEGER;
  1557.  
  1558. BEGIN
  1559.   mtAppl.MouseOn();
  1560.   mtAppl.MouseBusy();
  1561.   DEALLOCATE (groupArray, 0);
  1562.   groups := 0;
  1563.   maxGroups := 0;
  1564.   MagicStrings.Assign (DataPath, fname);
  1565.   MagicStrings.Append (groupDataName, fname);
  1566.   size := CatFiles.FileSize (fname, exists);
  1567.   IF exists & (size > 0) 
  1568.   THEN
  1569.     fhdl:= CatFiles.OpenFile (DataPath, groupDataName, CatFiles.readFile);
  1570.     IF fhdl > 0
  1571.     THEN
  1572.       IF ~parseData()
  1573.       THEN
  1574.         (* Einfach danach das CATGROUP.INF parsen, 
  1575.          * deshalb alles wieder freigeben 
  1576.          *)
  1577.         DEALLOCATE (groupArray, 0);
  1578.         groups := 0;
  1579.         maxGroups := 0;
  1580.         CatFiles.CloseFile (fhdl);
  1581.       ELSE
  1582.         CatFiles.CloseFile (fhdl);
  1583.         listModified := FALSE;
  1584.         mtAppl.MouseArrow();
  1585.         RETURN TRUE;
  1586.       END;
  1587.     END;
  1588.   END;
  1589.   MagicStrings.Assign (DataPath, fname);
  1590.   MagicStrings.Append (groupFileName, fname);
  1591.   size := CatFiles.FileSize (fname, exists);
  1592.   IF exists & (size > 0) & mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 32000, file)
  1593.   THEN
  1594.     IF ~parseLines()
  1595.     THEN
  1596.       mtAppl.MouseArrow();
  1597.       MTE.noMemAlert();
  1598.       mtTextfiles.CloseTextfile (file);
  1599.       RETURN FALSE;
  1600.     END;
  1601.     mtTextfiles.CloseTextfile (file);
  1602.     listModified := TRUE;
  1603.     mtAppl.MouseArrow();
  1604.     RETURN TRUE;
  1605.   ELSE
  1606.     (* File kann nicht ge”ffnet werden. Dann versuchen wir es mal 
  1607.      * anders und bauen das neu auf aus der GRUPPEN.INF und der 
  1608.      * ITG bzw. IGK
  1609.      *)
  1610.     MagicStrings.Assign (DataPath, fname);
  1611.     MagicStrings.Append (catGroupInf, fname);
  1612.     size := CatFiles.FileSize (fname, exists);
  1613.     IF exists & (size > 0) & 
  1614.        mtTextfiles.OpenTextfile (fname, mtTextfiles.READ, 8192, file)
  1615.     THEN
  1616.       (* Erstmal hier die Zeilen z„hlen *)
  1617.       lc := 0;
  1618.       WHILE ~mtTextfiles.EndofText (file) DO
  1619.         mtTextfiles.ReadLn (file);
  1620.         INC (lc);
  1621.       END;
  1622.       (* Jetzt wieder an den Anfang gehen *)
  1623.       mtTextfiles.Reset (file);
  1624.       (* Jetzt die Zeilen im Infofile z„hlen *)
  1625.       INC (lc, CountInfofileLines ());
  1626.       (* Und jetzt allozieren wir den Speicher *)
  1627.       IF ~MakeNewGroupArray (TRUE, lc)
  1628.       THEN  
  1629.         mtTextfiles.CloseTextfile (file);
  1630.         mtAppl.MouseArrow();
  1631.         MTE.noMemAlert();
  1632.         RETURN FALSE;
  1633.       END;
  1634.       v.bool := ParseGruppenInf (file);
  1635.       listModified := TRUE;
  1636.       mtTextfiles.CloseTextfile (file);
  1637.       IF v.bool
  1638.       THEN
  1639.         (* Ok, Gruppen.inf geparst, jetzt noch ITG bzw. IGK parsen *)
  1640.         v.bool := ParseInfofile();
  1641.         v.bool := SaveGroupList();
  1642.         mtAppl.MouseArrow();
  1643.         RETURN TRUE;
  1644.       ELSE
  1645.         mtAppl.MouseArrow();
  1646.         MTE.noMemAlert();
  1647.         RETURN FALSE;
  1648.       END;
  1649.     ELSIF ~exists OR (size = 0)
  1650.     THEN
  1651.       (* Keine GRUPPEN.INF da, wahrscheinlich leere Database. 
  1652.        * Deshalb jetzt nur die Infofiles parsen 
  1653.        *)
  1654.       lc := CountInfofileLines ();
  1655.       (* Und jetzt allozieren wir den Speicher *)
  1656.       IF ~MakeNewGroupArray (TRUE, lc)
  1657.       THEN  
  1658.         mtTextfiles.CloseTextfile (file);
  1659.         mtAppl.MouseArrow();
  1660.         MTE.noMemAlert();
  1661.         RETURN FALSE;
  1662.       END;
  1663.       v.bool := ParseInfofile();
  1664.       v.bool := SaveGroupList();
  1665.       mtAppl.MouseArrow();
  1666.       RETURN TRUE;
  1667.     ELSE
  1668.       (* Fehlermeldung rauswerfen *)
  1669.       mtAppl.MouseArrow();
  1670.       MTE.noMemAlert ();
  1671.       RETURN FALSE;
  1672.     END;
  1673.   END;
  1674.   mtAppl.MouseArrow();
  1675.   RETURN FALSE;
  1676. END ReadGroupList;
  1677.  
  1678. (* Funktionen, die fr das Zusammenstellen der neuen Listen ben”tigt werden *)
  1679.  
  1680. PROCEDURE CopyEntry (entry: groupPtr; VAR l : Lists.List): BOOLEAN;
  1681. (* kopiert einen Eintrag in die Liste l 
  1682.  *)
  1683.   VAR new : groupPtr;
  1684.       fail : BOOLEAN;
  1685. BEGIN
  1686.   NEW (new); 
  1687.   IF new = NIL THEN RETURN FALSE END;
  1688.   new^ := entry^;
  1689.   new^.data := NIL;
  1690.   WITH new^ DO
  1691.     ALLOCATE (data, entry^.dataLength);
  1692.     IF data = NIL THEN DISPOSE (new); RETURN FALSE END;
  1693.     Block.Copy (entry^.data, entry^.dataLength, data);
  1694.     IF entry^.name # NIL THEN  name := data + entry^.name - entry^.data; ELSE name := NIL; END;
  1695.     IF entry^.netname # NIL THEN  netname := data + entry^.netname - entry^.data; ELSE netname := NIL; END;
  1696.     IF entry^.chef # NIL THEN  chef := data + entry^.chef - entry^.data; ELSE chef := NIL; END;
  1697.     IF entry^.kurztext # NIL THEN  kurztext := data + entry^.kurztext - entry^.data; ELSE kurztext := NIL; END;
  1698.     IF entry^.sysop  # NIL THEN  sysop := data + entry^.sysop - entry^.data; ELSE sysop := NIL; END;
  1699.     IF entry^.followup # NIL THEN  followup := data + entry^.followup - entry^.data; ELSE followup := NIL; END;
  1700.     IF entry^.herkunft # NIL THEN  herkunft := data + entry^.herkunft - entry^.data; ELSE herkunft := NIL; END;
  1701.     IF entry^.sprache # NIL THEN  sprache := data + entry^.sprache - entry^.data; ELSE sprache := NIL; END;
  1702.     IF entry^.alias   # NIL THEN  alias   := data + entry^.alias   - entry^.data; ELSE alias   := NIL; END;
  1703.     dataLength := entry^.dataLength;
  1704.   END;
  1705.   Lists.AppendEntry (l, new, fail);
  1706.   RETURN ~fail;
  1707. END CopyEntry;
  1708.  
  1709. PROCEDURE CopyList (VAR list: Lists.List; doCopy : mayCopyProc): BOOLEAN;
  1710. (* Kopiert die Gruppenliste in eine neue Liste. Dabei wird ein Eintrag
  1711.  * genau dann kopiert, wenn doCopy TRUE liefert.
  1712.  *)
  1713.  VAR fail : BOOLEAN;
  1714.      run  : groupPtr;
  1715.      idx  : INTEGER;
  1716. BEGIN
  1717.   Lists.CreateList (list, fail);
  1718.   IF fail THEN RETURN FALSE END;
  1719.   FOR idx := 0 TO groups - 1 DO
  1720.     run := ADR (groupArray^[idx]);
  1721.     (* Testen, ob es eine CAT-Gruppe ist *)
  1722.     IF doCopy (run)
  1723.     THEN
  1724.       (* Eintrag kopieren(!!) und anh„ngen an neue Liste *)
  1725.       IF ~CopyEntry (run, list)
  1726.       THEN
  1727.         DeleteList (list);
  1728.         RETURN FALSE
  1729.       END;
  1730.     END;
  1731.   END;
  1732.   RETURN TRUE;
  1733. END CopyList;
  1734.  
  1735. (* hier kommen jetzt die einzelnen Vergleichsfunktionen fr CopyList
  1736.  *)
  1737. PROCEDURE GetNew (catNum: INTEGER): CARDINAL;
  1738.   VAR newIdx,
  1739.       lastIdx   : CARDINAL;
  1740. BEGIN
  1741.   IF catNum < 0 
  1742.   THEN
  1743.     RETURN 0;
  1744.   END;
  1745.   newIdx := data.FirstNewMsg (catNum);
  1746.   lastIdx := data.LastMsgOfGroup (catNum);
  1747.   IF (lastIdx # dataSys.empty) & (newIdx # dataSys.empty) & (newIdx <= lastIdx) 
  1748.   THEN RETURN lastIdx - newIdx+1; 
  1749.   ELSE RETURN 0
  1750.   END;
  1751. END GetNew;
  1752.  
  1753. PROCEDURE copyByNum (entry: groupPtr): BOOLEAN;
  1754. BEGIN
  1755.   RETURN entry^.catNumber >= 0;
  1756. END copyByNum;
  1757.  
  1758. PROCEDURE copyNew (entry: groupPtr): BOOLEAN;
  1759.  VAR  new : CARDINAL;
  1760.       catNum: INTEGER;
  1761. BEGIN
  1762.   catNum := entry^.catNumber;
  1763.   RETURN (GetNew (catNum) > 0);
  1764. END copyNew;
  1765.  
  1766. PROCEDURE copyUnread (entry: groupPtr): BOOLEAN;
  1767.   VAR unrd  : CARDINAL;
  1768.       catNum: INTEGER;
  1769. BEGIN
  1770.   catNum := entry^.catNumber;
  1771.   IF catNum < 0 
  1772.   THEN
  1773.     RETURN FALSE;
  1774.   END;
  1775.   unrd := data.unreadMsgCount(catNum);
  1776.   IF unrd = 0FFFFH THEN unrd := 0 END;
  1777.   RETURN (unrd > 0);
  1778. END copyUnread;
  1779.  
  1780. PROCEDURE copyUser (entry: groupPtr): BOOLEAN;
  1781. BEGIN
  1782.   RETURN (entry^.userState = uNA) OR (entry^.userState = uIsChef) OR 
  1783.          (isSysop & ~((entry^.isNet = nNetLokal) OR (entry^.isNet = nNetForbid)));
  1784. END copyUser;
  1785.  
  1786. PROCEDURE copyLokal (entry: groupPtr): BOOLEAN;
  1787. BEGIN
  1788.   RETURN ~((entry^.isNet = nNetLokal) OR (entry^.isNet = nNetForbid));
  1789. END copyLokal;
  1790.  
  1791. PROCEDURE copyWrite (entry: groupPtr): BOOLEAN;
  1792. BEGIN
  1793.   IF fullList
  1794.   THEN
  1795.     RETURN (entry^.writeState = rwNA) OR (entry^.writeState = rwOn) OR (entry^.writeState = rwPossible);
  1796.   ELSE
  1797.     RETURN (entry^.catNumber > 0) & 
  1798.            ((entry^.writeState = rwNA) OR (entry^.writeState = rwOn) OR (entry^.writeState = rwPossible));
  1799.   END;
  1800. END copyWrite;
  1801.  
  1802. PROCEDURE copyAll (entry: groupPtr): BOOLEAN;
  1803. BEGIN
  1804.   RETURN TRUE;
  1805. END copyAll;
  1806.  
  1807. (* Hier kommen jetzt Funktionen, die einzelne Listen oder Teillisten 
  1808.  * den anderen Modulen zur Verfgung stellen.
  1809.  *)
  1810.  
  1811. PROCEDURE GetReadList (VAR list : Lists.List; byReadNum: BOOLEAN; getNew, getUnread: BOOLEAN): BOOLEAN;
  1812. (* Diese Prozedur liefert eine Liste der Gruppen, die in CAT vorhanden
  1813.  * und damit lesbar sind. Die Gruppe 'PERS™NLICHE' ist mit enthalten.
  1814.  * Die Liste ist nach der Lesereihenfolge sortiert, die Gruppe 'PERS™NLICHE'
  1815.  * ist immer als erste Gruppe vorhanden (die Lesenummer davon kann nicht 
  1816.  * ge„ndert werden).
  1817.  * FALSE: Liste konnte nicht erzeugt werden
  1818.  *)
  1819.  VAR fail : BOOLEAN;
  1820.      run,
  1821.      new  : groupPtr;
  1822.      unrdCnt : CARDINAL;
  1823.      copyPers: BOOLEAN;
  1824.      
  1825. BEGIN
  1826.   mtAppl.MouseBusy();
  1827.   IF getNew & ~getUnread
  1828.   THEN
  1829.     IF ~CopyList (list, copyNew) THEN mtAppl.MouseArrow(); RETURN FALSE; END;
  1830.   ELSIF ~getNew & getUnread
  1831.   THEN
  1832.     IF ~CopyList (list, copyUnread) THEN mtAppl.MouseArrow(); RETURN FALSE; END;
  1833.   ELSE
  1834.     IF ~CopyList (list, copyByNum) THEN mtAppl.MouseArrow(); RETURN FALSE; END;
  1835.   END;
  1836.   (* Jetzt haben wir eine neue Liste, die wir nur noch nach der 
  1837.    * Lesereihenfolge sortieren mssen *)
  1838.   IF byReadNum
  1839.   THEN
  1840.     SortList (list, sByReadNum);  (* Hier eventuell noch Sortierprozedur bergeben *)
  1841. (*  ELSE
  1842.     SortList (list, sByName);  (* Hier eventuell noch Sortierprozedur bergeben *)
  1843. *)  END;
  1844.   (* So, jetzt haben wir eine sortierte Liste. Jetzt mssen wir nur 
  1845.    * noch die pers”nliche Gruppe evtl. dazu eintragen und vorne einfgen.
  1846.    *)
  1847.   IF getNew & ~getUnread
  1848.   THEN
  1849.     copyPers := GetNew (dataSys.private) > 0;
  1850.   ELSIF ~getNew & getUnread
  1851.   THEN
  1852.     unrdCnt := data.unreadMsgCount(dataSys.private);
  1853.     copyPers := (unrdCnt > 0) & (unrdCnt < 0FFFFH);
  1854.   ELSE
  1855.     copyPers := TRUE;
  1856.   END;
  1857.   IF copyPers
  1858.   THEN
  1859.     NEW (new);
  1860.     IF new = NIL THEN 
  1861.       DeleteList (list);
  1862.       mtAppl.MouseArrow();
  1863.       RETURN FALSE 
  1864.     END;
  1865.     WITH new^ DO
  1866.       ALLOCATE (data, LENGTH (dataSys.personalName)+2);
  1867.       IF data = NIL THEN 
  1868.         DeleteList (list);
  1869.         mtAppl.MouseArrow();
  1870.         RETURN FALSE 
  1871.       END;
  1872.       dataLength := LENGTH (dataSys.personalName) +2;
  1873.       name := data;
  1874.       Strings.Assign (dataSys.personalName, name^, fail);
  1875.       netname := NIL;
  1876.       chef    := NIL;
  1877.       kurztext  := NIL;
  1878.       sysop     := NIL;
  1879.       followup  := NIL;
  1880.       herkunft  := NIL;
  1881.       sprache   := NIL;
  1882.       alias     := NIL;
  1883.       readNumber:= 0;
  1884.       catNumber := dataSys.private;
  1885.       readState := rwOn;
  1886.       writeState:= rwImpossible;
  1887.       userState := uNA;
  1888.       isNet     := nNA;
  1889.       ordered   := oNA;
  1890.       orderDays := 0;
  1891.       hasProg   := FALSE;
  1892.       withDollar:= FALSE;
  1893.       selected  := FALSE;
  1894.     END;
  1895.     Lists.ResetList (list);
  1896.     Lists.InsertEntry (list, new, fail);
  1897.     IF fail THEN 
  1898.       DEALLOCATE (new^.data, 0);
  1899.       DISPOSE (new);
  1900.       DeleteList (list);
  1901.       mtAppl.MouseArrow();
  1902.       RETURN FALSE 
  1903.     END;  
  1904.   END;
  1905.   mtAppl.MouseArrow();
  1906.   RETURN TRUE;
  1907. END GetReadList;
  1908.  
  1909. PROCEDURE UpdateReadList (list: Lists.List): BOOLEAN;
  1910. (* Hiermit wird das Modul aufgefordert, die Lesereihenfolge zu „ndern. 
  1911.  * Es muž als Parameter eine Liste bergeben werden, die eine ge„nderte 
  1912.  * Lesereihenfolge hat und die ber GetReadList angefordert wurde.
  1913.  * FALSE: Interne Liste konnte nicht upgedatet werden
  1914.  *)
  1915.   VAR gr, 
  1916.       run   : groupPtr;
  1917.       grIdx : INTEGER;
  1918. BEGIN
  1919.   mtAppl.MouseBusy();
  1920.   Lists.ResetList (list);
  1921.   run := Lists.NextEntry (list);
  1922.   WHILE run # NIL DO
  1923.     IF run^.name # NIL
  1924.     THEN
  1925.       IF findEntry (run^.name^, grIdx)
  1926.       THEN
  1927.         IF groupArray^[grIdx].readNumber # run^.readNumber
  1928.         THEN
  1929.           groupArray^[grIdx].readNumber := run^.readNumber;
  1930.           listModified := TRUE;
  1931.         END;
  1932.       END;
  1933.     END;
  1934.     run := Lists.NextEntry (list);
  1935.   END;
  1936.   mtAppl.MouseArrow();
  1937.   RETURN TRUE;
  1938. END UpdateReadList;
  1939.  
  1940. PROCEDURE GetUserList (VAR list: Lists.List): BOOLEAN;
  1941. (* Diese Prozedur liefert eine Liste der Gruppen, in denen man Chef ist.
  1942.  * Diese wird ben”tigt, um User in andere Gruppen einzutragen.
  1943.  * FALSE: Liste konnte nicht erzeugt werden
  1944.  *)
  1945.  VAR fail : BOOLEAN;
  1946. BEGIN
  1947.   mtAppl.MouseBusy();
  1948.   (* Sysop feststellen *)
  1949.   isSysop := Infofiles.IsInfoFile ('ISB', TRUE);
  1950.   fail := CopyList (list, copyUser);
  1951.   mtAppl.MouseArrow();
  1952.   RETURN fail;
  1953. END GetUserList;
  1954.  
  1955. PROCEDURE GetWriteList (VAR list: Lists.List; full: BOOLEAN): BOOLEAN;
  1956. (* Diese Prozedur liefert eine Liste der Gruppen, in denen man schreiben 
  1957.  * kann. Diese Liste wird dann benutzt, wenn man eine neue Nachricht in 
  1958.  * eine Gruppe schreiben will.
  1959.  * FALSE: Liste konnte nicht erzeugt werden
  1960.  *)
  1961.  VAR fail : BOOLEAN;
  1962.      new  : groupPtr;
  1963. BEGIN
  1964.   mtAppl.MouseBusy();
  1965.   fullList := full;
  1966.   fail := CopyList (list, copyWrite);
  1967.   IF ~fail THEN mtAppl.MouseArrow(); RETURN FALSE END;
  1968.   NEW (new);
  1969.   IF new = NIL THEN 
  1970.     DeleteList (list);
  1971.     mtAppl.MouseArrow();
  1972.     RETURN FALSE 
  1973.   END;
  1974.   WITH new^ DO
  1975.     ALLOCATE (data, LENGTH (dataSys.personalName)+2);
  1976.     IF data = NIL THEN 
  1977.       DeleteList (list);
  1978.       mtAppl.MouseArrow();
  1979.       RETURN FALSE 
  1980.     END;
  1981.     dataLength := LENGTH (dataSys.personalName) +2;
  1982.     name := data;
  1983.     Strings.Assign (dataSys.personalName, name^, fail);
  1984.     netname := NIL;
  1985.     chef    := NIL;
  1986.     kurztext  := NIL;
  1987.     sysop     := NIL;
  1988.     followup  := NIL;
  1989.     herkunft  := NIL;
  1990.     sprache   := NIL;
  1991.     alias     := NIL;
  1992.     readNumber:= 0;
  1993.     catNumber := dataSys.private;
  1994.     readState := rwOn;
  1995.     writeState:= rwImpossible;
  1996.     userState := uNA;
  1997.     isNet     := nNA;
  1998.     ordered   := oNA;
  1999.     orderDays := 0;
  2000.     hasProg   := FALSE;
  2001.     withDollar:= FALSE;
  2002.     selected  := FALSE;
  2003.   END;
  2004.   Lists.ResetList (list);
  2005.   Lists.InsertEntry (list, new, fail);
  2006.   mtAppl.MouseArrow();
  2007.   IF fail THEN 
  2008.     DEALLOCATE (new^.data, 0);
  2009.     DISPOSE (new);
  2010.     DeleteList (list);
  2011.     RETURN FALSE 
  2012.   END;  
  2013.   RETURN TRUE;
  2014. END GetWriteList;
  2015.  
  2016. PROCEDURE GetSelectList (VAR list: Lists.List; lokal: BOOLEAN;
  2017.                          sortBy: selSortType): BOOLEAN;
  2018. (* Diese Prozedur liefert eine Liste der Gruppen, die in der Maus vorhanden
  2019.  * sind bzw. alle Gruppen aus dem ITG. Benutzt wird diese Liste fr die 
  2020.  * Gruppenauswahl.
  2021.  * lokal: Es werden nur die lokal vorhandenen Gruppen geliefert
  2022.  * FALSE: Liste konnte nicht erzeugt werden
  2023.  *)
  2024. BEGIN
  2025.   mtAppl.MouseBusy();
  2026.   IF lokal
  2027.   THEN
  2028.     IF ~CopyList (list, copyLokal) THEN mtAppl.MouseArrow(); RETURN FALSE END;
  2029.   ELSE
  2030.     IF ~CopyList (list, copyAll) THEN mtAppl.MouseArrow(); RETURN FALSE END;
  2031.   END;
  2032.   (* Nun noch sortieren *)
  2033.   CASE sortBy OF
  2034.     sName : (* Ist per Default nach Namen sortiert *) |
  2035.     sNet  : SortList (list, sByNet);  |
  2036.     sLang : SortList (list, sByLang); |
  2037.   ELSE
  2038.   END;
  2039.   mtAppl.MouseArrow();
  2040.   RETURN TRUE;
  2041. END GetSelectList;
  2042.  
  2043. PROCEDURE UpdateSelectList (list: Lists.List; resort: BOOLEAN): BOOLEAN;
  2044. (* Hiermit wird das Modul aufgefordert, sich zu merken, welche Gruppen
  2045.  * beim n„chsten Tausch bestellt oder abbestellt werden sollen. 
  2046.  * Es muž als Parameter eine Liste bergeben werden,
  2047.  * die ber GetSelectList angefordert wurde 
  2048.  * FALSE: Interne Liste konnte nicht upgedatet werden
  2049.  *)
  2050.   VAR gr, 
  2051.       run   : groupPtr;
  2052.       grIdx : INTEGER;
  2053. BEGIN
  2054.   mtAppl.MouseBusy();
  2055.   Lists.ResetList (list);
  2056.   run := Lists.NextEntry (list);
  2057.   WHILE run # NIL DO
  2058.     IF run^.name # NIL
  2059.     THEN
  2060.       IF findEntry (run^.name^, grIdx)
  2061.       THEN
  2062.         gr := ADR (groupArray^[grIdx]);
  2063.         IF (gr^.ordered # run^.ordered) OR (gr^.orderDays # run^.orderDays)
  2064.         THEN
  2065.           gr^.ordered := run^.ordered;
  2066.           gr^.orderDays := run^.orderDays;
  2067.           listModified := TRUE;
  2068.         END;
  2069.         IF (run^.followup # NIL)
  2070.         THEN
  2071.           IF ((gr^.followup # NIL) & ~AssFuncs.StrIequal (run^.followup^, gr^.followup^))
  2072.             OR (gr^.followup = NIL)
  2073.           THEN
  2074.             (* Followup setzen *)
  2075.             SetFollowUp (gr, run^.followup^);
  2076.             listModified := TRUE;
  2077.           END;
  2078.         ELSIF (gr^.followup # NIL)
  2079.         THEN
  2080.           (* Followup wurde gel”scht *)
  2081.           SetFollowUp (gr, "");
  2082.           listModified := TRUE;
  2083.         END;
  2084.       END;
  2085.     END;
  2086.     run := Lists.NextEntry (list);
  2087.   END;
  2088.   mtAppl.MouseArrow();
  2089.   RETURN TRUE;
  2090. END UpdateSelectList;
  2091.  
  2092. PROCEDURE DeleteList (VAR list: Lists.List);
  2093. (* L”scht eine Liste vollst„ndig, also auch die Wurzel davon. Danach darf
  2094.  * nicht mehr auf die Liste zugegriffen werden.
  2095.  *)
  2096.   VAR entry: groupPtr;
  2097.       fail : BOOLEAN;
  2098. BEGIN
  2099.   mtAppl.MouseBusy();
  2100.   Lists.ResetList (list);
  2101.   entry := Lists.PrevEntry (list);
  2102.   WHILE entry # NIL DO
  2103.     Lists.RemoveEntry (list, v.bool);
  2104.     DEALLOCATE (entry^.data, 0L);
  2105.     DEALLOCATE (entry, 0L);
  2106.     entry := Lists.CurrentEntry (list);
  2107.   END;
  2108.   Lists.DeleteList (list, fail);
  2109.   mtAppl.MouseArrow();
  2110. END DeleteList;
  2111.  
  2112. (* Jetzt noch ein paar Prozeduren fr data *)
  2113. PROCEDURE NameToFile (REF name : ARRAY OF CHAR; REF path, fname : ARRAY OF CHAR):BOOLEAN;
  2114. (*$W-*)
  2115. (* wg. mtTextfiles.WriteLine der erste Parameter als VAR.. *)
  2116. (* 'name' in angegebene Datei schreiben, fr Absender- und Gruppennamen *)
  2117. VAR out : INTEGER;
  2118. BEGIN
  2119.   out := CatFiles.OpenFile(path, fname, CatFiles.writeFile);
  2120.   IF out > 0 THEN
  2121.     CatFiles.Seek(0, out, CatFiles.end);
  2122.     CatFiles.WriteMuch(LONG(LENGTH(name)), out, ADR(name));
  2123.     CatFiles.WriteFile(CR, out); CatFiles.WriteFile(LF, out);
  2124.     CatFiles.CloseFile(out);
  2125.     RETURN CatFiles.FileError = 0
  2126.   ELSE
  2127.     MTE.info(MTE.nameNotWritten);
  2128.     RETURN FALSE
  2129.   END;
  2130. END NameToFile;
  2131. (*$W=*)
  2132.  
  2133. PROCEDURE SaveGruppenInf ();
  2134. (* Sichert die Liste der Gruppen komplett 
  2135.  *)
  2136.  VAR file : CatTypes.String255;
  2137.      out  : mtTextfiles.TEXTFILE;
  2138.      num, 
  2139.      i    : INTEGER;
  2140.      idx  : CARDINAL;
  2141.      tmp  : CatTypes.String255;
  2142. BEGIN
  2143.   MagicStrings.Assign (DataPath, file);
  2144.   MagicStrings.Append (catGroupInf, file);
  2145.   IF mtTextfiles.OpenTextfile (file, mtTextfiles.WRITE, 4096, out)
  2146.   THEN
  2147.     countCatEntries (num);
  2148.     FOR i := 1 TO num  DO
  2149.       IF findNumberEntry (i, idx)
  2150.       THEN
  2151.         IF (groupArray^[idx].name # NIL)
  2152.         THEN
  2153.           MagicStrings.Assign (groupArray^[idx].name^, tmp);
  2154.           MagicStrings.CAPS (tmp);
  2155.           mtTextfiles.WriteLine (out, tmp);
  2156.           mtTextfiles.WriteLn(out);
  2157.         END;
  2158.       END;
  2159.     END;
  2160.     mtTextfiles.CloseTextfile (out);
  2161.     Protokoll.SendPathUpdate (DataPath);
  2162.   END;
  2163. END SaveGruppenInf;
  2164.  
  2165. PROCEDURE RenameGroup (REF oldName, newName : ARRAY OF CHAR; extern: BOOLEAN): BOOLEAN;
  2166. (* Benennt eine Gruppe um. Die GRUPPEN.INF wird danach direkt neu 
  2167.  * geschrieben, die Žnderungen in den anderen Daten werden 
  2168.  * am Programmende gesichert
  2169.  *)
  2170.   VAR found : BOOLEAN;
  2171.       idx   : INTEGER;
  2172.       idx2  : INTEGER;
  2173.       avail : lineSet;
  2174.       void,
  2175.       gName, nName, uLine, cLine, sLine, rLine, hLine, pLine, aLine : CatTypes.String255;
  2176.       gr    : groupPtr;
  2177. BEGIN
  2178.   (* Pers”nliche l„žt sich nicht umbenennen *)
  2179.   IF AssFuncs.StrIequal (oldName, newName) THEN RETURN TRUE; END;
  2180.   IF AssFuncs.StrIequal(oldName, dataSys.personalName) THEN RETURN FALSE END;
  2181.   found := findEntry (oldName, idx);
  2182.   IF found
  2183.   THEN
  2184.     IF findEntry (newName, idx2)
  2185.     THEN
  2186.       IF groupArray^[idx2].catNumber >= 0
  2187.       THEN
  2188.         (* Gruppe ist in CAT schon vorhanden *)
  2189.         RETURN FALSE
  2190.       END;
  2191.       (* Es sind beide Gruppennamen vorhanden. D.h. der Eintrag newName
  2192.        * erh„lt einfach die CAT-Infos zugewiesen, am Eintrag selber
  2193.        * wird aber nichts ge„ndert.
  2194.        *)
  2195.       gr := ADR(groupArray^[idx]);
  2196.       WITH groupArray^[idx2] DO
  2197.         readNumber := gr^.readNumber;
  2198.         catNumber := gr^.catNumber;
  2199.         gr^.readNumber := -1;
  2200.         gr^.catNumber := -1;
  2201.       END;
  2202.       listModified := TRUE;
  2203.       (* Jetzt die ge„nderte GRUPPEN.INF speichern *)
  2204.       SaveGruppenInf();
  2205.       (* Jetzt das Array neu sortieren! *)
  2206.       SortGroupArray();
  2207.       (* Und jetzt raus hier *)
  2208.       RETURN TRUE;
  2209.     END;
  2210.     gr := ADR(groupArray^[idx]);
  2211.     avail := lineSet {gL, cN, rN, oN, dN};
  2212.     MagicStrings.Assign (newName, gName);
  2213.     MagicStrings.Assign ('', nName);
  2214.     MagicStrings.Assign ('', uLine);
  2215.     MagicStrings.Assign ('', cLine);
  2216.     MagicStrings.Assign ('', sLine);
  2217.     MagicStrings.Assign ('', rLine);
  2218.     MagicStrings.Assign ('', hLine);
  2219.     MagicStrings.Assign ('', pLine);
  2220.     MagicStrings.Assign ('', aLine);
  2221.     IF gr^.netname # NIL
  2222.     THEN
  2223.       (* Netname zuweisen *)
  2224.       MagicStrings.Assign (gr^.netname^, nName);
  2225.       INCL (avail, nL);
  2226.     END;
  2227.     IF (gr^.kurztext # NIL)
  2228.     THEN
  2229.       (* Netname zuweisen *)
  2230.       MagicStrings.Assign (gr^.kurztext^, uLine);
  2231.       INCL (avail, uL);
  2232.     END;
  2233.     IF (gr^.chef # NIL)
  2234.     THEN
  2235.       (* Netname zuweisen *)
  2236.       MagicStrings.Assign (gr^.chef^, cLine);
  2237.       INCL (avail, cL);
  2238.     END;
  2239.     IF (gr^.sysop # NIL)
  2240.     THEN
  2241.       (* Netname zuweisen *)
  2242.       MagicStrings.Assign (gr^.sysop^, sLine);
  2243.       INCL (avail, sL);
  2244.     END;
  2245.     IF (gr^.followup # NIL)
  2246.     THEN
  2247.       (* Followup zuweisen *)
  2248.       MagicStrings.Assign (gr^.followup^, rLine);
  2249.       INCL (avail, rL);
  2250.     END;
  2251.     IF (gr^.herkunft # NIL)
  2252.     THEN
  2253.       (* Herkunft zuweisen *)
  2254.       MagicStrings.Assign (gr^.herkunft^, hLine);
  2255.       INCL (avail, hL);
  2256.     END;
  2257.     IF (gr^.sprache # NIL)
  2258.     THEN
  2259.       (* Sprache zuweisen *)
  2260.       MagicStrings.Assign (gr^.sprache^, pLine);
  2261.       INCL (avail, pL);
  2262.     END;
  2263.     IF (gr^.alias  # NIL)
  2264.     THEN
  2265.       (* Alias zuweisen *)
  2266.       MagicStrings.Assign (gr^.alias^, aLine);
  2267.       INCL (avail, aL);
  2268.     END;
  2269.     IF ~MakeEntry (gr, gName, nName, uLine, cLine, void, sLine, rLine, hLine, pLine, aLine, gr^.readNumber, gr^.catNumber, gr^.ordered, gr^.orderDays, FALSE, avail)
  2270.     THEN 
  2271.       RETURN FALSE
  2272.     END;
  2273.     listModified := TRUE;
  2274.     (* Jetzt die ge„nderte GRUPPEN.INF speichern *)
  2275.     SaveGruppenInf();
  2276.     (* Jetzt das Array neu sortieren! *)
  2277.     SortGroupArray();
  2278.   END;
  2279.   RETURN found;
  2280. END RenameGroup;
  2281.  
  2282. PROCEDURE AppendGroup (VAR groupName : ARRAY OF CHAR):BOOLEAN;
  2283. (* Neuen Gruppennamen bernehmen, meckert selber, falls es nicht klappt *)
  2284.   VAR new, 
  2285.       gr    : groupPtr;
  2286.       fail,
  2287.       found : BOOLEAN;
  2288.       catNums : INTEGER;
  2289.       void     : CatTypes.String127;
  2290.       testName : CatTypes.String255;
  2291.       linesAvail : lineSet;
  2292.       lastReadNum: INTEGER;
  2293.       i,
  2294.       idx        : INTEGER;
  2295.       newGroup   : group;
  2296.       compRes    : MagicStrings.Relation;
  2297.       
  2298. BEGIN
  2299.   catNums := 0;
  2300.   countCatEntries (catNums);
  2301.   catNums := catNums + 1;
  2302.   lastReadNum := 0;
  2303.   idx := 0;
  2304.   found := FALSE;
  2305.   REPEAT
  2306.     gr := ADR(groupArray^[idx]);
  2307.     IF (gr^.name # NIL)
  2308.     THEN
  2309.       IF ~found
  2310.       THEN
  2311.         compRes := AssFuncs.StrICompare (gr^.name^, groupName);
  2312.         IF compRes = MagicStrings.equal
  2313.         THEN
  2314.           found := TRUE;
  2315.           gr^.catNumber := catNums;
  2316.           gr^.readNumber := lastReadNum + 1;
  2317.         ELSIF compRes = MagicStrings.greater
  2318.         THEN
  2319.           (* neuen Eintrag anlegen *)
  2320.           IF ~MakeNewGroupArray (FALSE, 0)
  2321.           THEN RETURN FALSE END;
  2322.           newGroup := group{NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,-1,-1,rwNA,rwNA,uNA,nNA,
  2323.                             FALSE,FALSE,FALSE,oNA,0,TRUE,0,NIL};
  2324.           gr := ADR (newGroup);
  2325.  
  2326.           (* Hier jetzt einfgen, da er hierhin pažt! *)
  2327.           linesAvail := lineSet{gL, cN, rN, oN};
  2328.           MagicStrings.Assign ('', void);
  2329.           IF ~MakeEntry (gr, groupName, void, void, void, void, void, void, void, void, void, lastReadNum + 1, catNums, oNA, 0, TRUE, linesAvail)
  2330.           THEN
  2331.             RETURN FALSE
  2332.           END;
  2333.           IF groups > 0
  2334.           THEN
  2335.             (* Alle nachfolgenden verschieben *)
  2336.             FOR i := groups TO idx + 1 BY -1 DO
  2337.               groupArray^[i] := groupArray^[i-1]
  2338.             END;
  2339.           END;
  2340.           (* Eintrag jetzt bernehmen *)
  2341.           groupArray^[idx] := gr^;
  2342.           INC (groups);
  2343.           found := TRUE;
  2344.         ELSIF gr^.catNumber >= 0
  2345.         THEN
  2346.           lastReadNum := gr^.readNumber
  2347.         END;
  2348.       END;
  2349.     END;
  2350.     INC (idx);
  2351.   UNTIL (idx >= groups) OR (found);
  2352.   IF ~found
  2353.   THEN
  2354.     (* Wahrscheinlich leere Liste, Eintrag bei Index groups anlegen 
  2355.      *)
  2356.     (* neuen Eintrag anlegen *)
  2357.     IF ~MakeNewGroupArray (FALSE, 0)
  2358.     THEN RETURN FALSE END;
  2359.     newGroup := group{NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,NIL,-1,-1,rwNA,rwNA,uNA,nNA,
  2360.                       FALSE,FALSE,FALSE,oNA,0,TRUE,0,NIL};
  2361.     gr := ADR (newGroup);
  2362.  
  2363.     (* Hier jetzt einfgen, da er hierhin pažt! *)
  2364.     linesAvail := lineSet{gL, cN, rN, oN};
  2365.     MagicStrings.Assign ('', void);
  2366.     IF ~MakeEntry (gr, groupName, void, void, void, void, void, void, void, void, void, lastReadNum + 1, catNums, oNA, 0, TRUE, linesAvail)
  2367.     THEN
  2368.       RETURN FALSE
  2369.     END;
  2370.     (* Eintrag jetzt bernehmen *)
  2371.     groupArray^[groups] := gr^;
  2372.     INC (groups);
  2373.   END;
  2374.   (* Jetzt die Lesenummern anpassen *)
  2375.   FOR idx := 0 TO groups - 1 DO
  2376.     gr := ADR (groupArray^[idx]);
  2377.     IF (gr^.catNumber >= 0) & (gr^.readNumber > lastReadNum) & (gr^.catNumber # catNums)
  2378.     THEN
  2379.       INC (gr^.readNumber);
  2380.     END;
  2381.   END;
  2382.   listModified := TRUE;
  2383.   (* Jetzt Namen an GRUPPEN.INF anh„ngen, dort immer grož geschrieben *)
  2384.   MagicStrings.CAPS (groupName);
  2385.   RETURN NameToFile(groupName, DataPath, catGroupInf);
  2386. END AppendGroup;
  2387.  
  2388. PROCEDURE GroupNumber (REF gName : ARRAY OF CHAR; VAR nr : CARDINAL):BOOLEAN;
  2389. (* Gruppennummer zu einem String feststellen *)
  2390. (* Wenn FALSE, dann kommt Gruppenanzahl in nr zurck, Anzahl      *)
  2391. (* inklusive pers”nliche                                          *)
  2392.   VAR found : BOOLEAN;
  2393.       idx   : INTEGER;
  2394.       iNr   : INTEGER;
  2395. BEGIN
  2396.   IF AssFuncs.StrIequal(gName, dataSys.personalName) THEN nr := 0; RETURN TRUE END;
  2397.   found := findEntry (gName, idx);
  2398.   IF found
  2399.   THEN
  2400.     IF groupArray^[idx].catNumber < 0
  2401.     THEN
  2402.       found := FALSE;
  2403.     ELSE
  2404.       nr := CARDINAL (groupArray^[idx].catNumber);
  2405.       RETURN TRUE
  2406.     END;
  2407.   END;
  2408.   IF ~found
  2409.   THEN
  2410.     countCatEntries (iNr);
  2411.     nr := iNr + 1;
  2412.   END;
  2413.   RETURN FALSE
  2414. END GroupNumber;
  2415.  
  2416. PROCEDURE CountCatGroups(): CARDINAL;
  2417. (* Liefert die Zahl der Gruppen in CAT zurck, inklusive der pers”nlichen Gruppe 
  2418.  *)
  2419.  VAR iNr : INTEGER;
  2420. BEGIN
  2421.   countCatEntries (iNr);
  2422.   RETURN iNr + 1;
  2423. END CountCatGroups;
  2424.  
  2425. PROCEDURE NextGroupNumber (old : CARDINAL):CARDINAL;
  2426. (* Nummer der n„chsten Gruppe in der Reihenfolge der momentanen Sortierung *)
  2427. (* feststellen; 0FFFF = empty falls keine weiter Gruppe vorhanden ist      *)
  2428.   VAR new : INTEGER;
  2429.       idx  : CARDINAL;
  2430. BEGIN
  2431.   new := -1;
  2432.   IF old # dataSys.private
  2433.   THEN
  2434.     IF findNumberEntry (old, idx)
  2435.     THEN
  2436.       old := groupArray^[idx].readNumber;
  2437.     END;
  2438.   END;
  2439.   IF findReadNumberEntry (old+1, idx)
  2440.   THEN 
  2441.     IF groupArray^[idx].catNumber >= 0
  2442.     THEN 
  2443.       new := groupArray^[idx].catNumber
  2444.     END;
  2445.   END;
  2446.   RETURN CARDINAL (new);
  2447. END NextGroupNumber;
  2448.  
  2449. PROCEDURE PreviousGroupNumber (old : CARDINAL):CARDINAL;
  2450. (* Nummer der vorhergehenden Gruppe in der Reihenfolge der momentanen Sortierung *)
  2451. (* feststellen; 0FFFF = empty falls keine weiter Gruppe vorhanden ist      *)
  2452.   VAR new : INTEGER;
  2453.       idx  : CARDINAL;
  2454. BEGIN
  2455.   new := -1;
  2456.   IF old # dataSys.private
  2457.   THEN
  2458.     IF findNumberEntry (old, idx)
  2459.     THEN
  2460.       old := groupArray^[idx].readNumber;
  2461.     END;
  2462.   ELSE
  2463.     RETURN 0FFFFH;
  2464.   END;
  2465.   IF findReadNumberEntry (old-1, idx)
  2466.   THEN 
  2467.     IF groupArray^[idx].catNumber >= 0
  2468.     THEN 
  2469.       new := groupArray^[idx].catNumber
  2470.     END;
  2471.   END;
  2472.   RETURN CARDINAL (new);
  2473. END PreviousGroupNumber;
  2474.  
  2475. PROCEDURE NetGroupNumber(REF gName : ARRAY OF CHAR; VAR nr : CARDINAL):BOOLEAN;
  2476. (* Gruppennummer zu einem String mit einem Netzgruppennamen feststellen *)
  2477. (* Wenn FALSE, dann kommt Gruppenanzahl in nr zurck, Anzahl      *)
  2478. (* inklusive pers”nliche                                          *)
  2479.   VAR found : BOOLEAN;
  2480.       idx   : INTEGER;
  2481.       iNr   : INTEGER;
  2482. BEGIN
  2483.   IF AssFuncs.StrIequal(gName, dataSys.personalName) THEN nr := 0; RETURN TRUE END;
  2484.   found := findNetEntry (gName, idx);
  2485.   IF found
  2486.   THEN
  2487.     IF groupArray^[idx].catNumber < 0
  2488.     THEN
  2489.       found := FALSE;
  2490.     ELSE
  2491.       nr := CARDINAL (groupArray^[idx].catNumber);
  2492.       RETURN TRUE
  2493.     END;
  2494.   END;
  2495.   IF ~found
  2496.   THEN
  2497.     countCatEntries (iNr);
  2498.     nr := iNr + 1;
  2499.   END;
  2500.   RETURN FALSE
  2501. END NetGroupNumber;
  2502.  
  2503. PROCEDURE GroupName (nr : CARDINAL; VAR name : ARRAY OF CHAR);
  2504. (* Gruppenname zu einer Nummer feststellen, liefert im Fehlerfall *)
  2505. (* keinen Eintrag *)
  2506.   VAR gr    : groupPtr;
  2507.       iNr   : INTEGER;
  2508.       idx   : CARDINAL;
  2509. BEGIN
  2510.   iNr := INTEGER (nr);
  2511.   MagicStrings.Assign ("", name);
  2512.   IF iNr = dataSys.private
  2513.   THEN
  2514.     MagicStrings.Assign (dataSys.personalName, name)
  2515.   ELSIF (iNr >= 0)
  2516.   THEN
  2517.     IF findNumberEntry (nr, idx)
  2518.     THEN
  2519.       IF (groupArray^[idx].name # NIL)
  2520.       THEN
  2521.         MagicStrings.Assign (groupArray^[idx].name^, name);
  2522.       END;
  2523.     END;
  2524.   END;
  2525. END GroupName;
  2526.  
  2527. VAR groupChangeIdx : INTEGER;
  2528.  
  2529. PROCEDURE GetGroupChange (VAR str : ARRAY OF CHAR; mode: gcMode): BOOLEAN;
  2530. (* Liefert bei TRUE in str einen String mit +gname oder -gname, 
  2531.  * je nachdem, ob eine Gruppe angestellt oder abgestellt werden soll
  2532.  *)
  2533.   VAR gr : groupPtr;
  2534. BEGIN
  2535.   MagicStrings.Assign ("", str);
  2536.   IF mode = GFIRST
  2537.   THEN
  2538.     groupChangeIdx := 0;
  2539.   ELSE
  2540.     INC (groupChangeIdx);
  2541.   END;
  2542.   WHILE (groupChangeIdx < groups) DO
  2543.     gr := ADR (groupArray^[groupChangeIdx]);
  2544.     IF (gr^.ordered # oNA)
  2545.      & (gr^.name # NIL)
  2546.     THEN
  2547.       IF gr^.ordered = oOn
  2548.       THEN
  2549.         MagicStrings.Assign ('+', str);
  2550.       ELSE
  2551.         MagicStrings.Assign ('-', str);
  2552.       END;
  2553.       MagicStrings.Append (gr^.name^, str);
  2554.       RETURN TRUE;
  2555.     END;
  2556.     INC (groupChangeIdx);
  2557.   END;
  2558.   RETURN FALSE;
  2559. END GetGroupChange;
  2560.  
  2561. PROCEDURE ClearGroupChange (VAR name : ARRAY OF CHAR);
  2562. (* L”scht eine Gruppen„nderung fr die Gruppe name
  2563.  *)
  2564.   VAR idx : INTEGER;
  2565. BEGIN
  2566.   IF findEntry (name, idx)
  2567.   THEN
  2568.     IF groupArray^[idx].ordered = oOn
  2569.     THEN
  2570.       groupArray^[idx].readState := rwOn;
  2571.     ELSIF groupArray^[idx].ordered = oOff
  2572.     THEN
  2573.       groupArray^[idx].readState := rwPossible;
  2574.     END;
  2575.     groupArray^[idx].ordered := oNA;
  2576.     groupArray^[idx].orderDays := 0;
  2577.     listModified := TRUE;
  2578.   END;
  2579. END ClearGroupChange;
  2580.  
  2581. PROCEDURE SetFollowUp (gr : groupPtr; REF name : ARRAY OF CHAR);
  2582. (* Setzt das Follow-Up fr die Gruppe group
  2583.  *)
  2584.   VAR avail : lineSet;
  2585.       void,
  2586.       gName, nName, uLine, cLine, sLine, rLine, hLine, pLine, aLine : CatTypes.String255;
  2587. BEGIN
  2588.   avail := lineSet {rL, cN, rN, oN, dN};
  2589.   MagicStrings.Assign ('', gName);
  2590.   MagicStrings.Assign ('', nName);
  2591.   MagicStrings.Assign ('', uLine);
  2592.   MagicStrings.Assign ('', cLine);
  2593.   MagicStrings.Assign ('', sLine);
  2594.   MagicStrings.Assign ('', hLine);
  2595.   MagicStrings.Assign ('', pLine);
  2596.   MagicStrings.Assign ('', aLine);
  2597.   IF gr^.name # NIL
  2598.   THEN
  2599.     MagicStrings.Assign (gr^.name^, gName);
  2600.     INCL (avail, gL);
  2601.   END;
  2602.   IF gr^.netname # NIL
  2603.   THEN
  2604.     (* Netname zuweisen *)
  2605.     MagicStrings.Assign (gr^.netname^, nName);
  2606.     INCL (avail, nL);
  2607.   END;
  2608.   IF (gr^.kurztext # NIL)
  2609.   THEN
  2610.     (* Netname zuweisen *)
  2611.     MagicStrings.Assign (gr^.kurztext^, uLine);
  2612.     INCL (avail, uL);
  2613.   END;
  2614.   IF (gr^.chef # NIL)
  2615.   THEN
  2616.     (* Netname zuweisen *)
  2617.     MagicStrings.Assign (gr^.chef^, cLine);
  2618.     INCL (avail, cL);
  2619.   END;
  2620.   IF (gr^.sysop # NIL)
  2621.   THEN
  2622.     (* Netname zuweisen *)
  2623.     MagicStrings.Assign (gr^.sysop^, sLine);
  2624.     INCL (avail, sL);
  2625.   END;
  2626.   IF (gr^.herkunft # NIL)
  2627.   THEN
  2628.     (* Netname zuweisen *)
  2629.     MagicStrings.Assign (gr^.herkunft^, hLine);
  2630.     INCL (avail, hL);
  2631.   END;
  2632.   IF (gr^.sprache # NIL)
  2633.   THEN
  2634.     (* Netname zuweisen *)
  2635.     MagicStrings.Assign (gr^.sprache^, pLine);
  2636.     INCL (avail, pL);
  2637.   END;
  2638.   IF (gr^.alias # NIL)
  2639.   THEN
  2640.     (* Alias zuweisen *)
  2641.     MagicStrings.Assign (gr^.alias^, aLine);
  2642.     INCL (avail, aL);
  2643.   END;
  2644.   MagicStrings.Assign (name, rLine);
  2645.   IF ~MakeEntry (gr, gName, nName, uLine, cLine, void, sLine, rLine, hLine, pLine, aLine, gr^.readNumber, gr^.catNumber, gr^.ordered, gr^.orderDays, FALSE, avail)
  2646.   THEN 
  2647.   END;
  2648. END SetFollowUp;
  2649.  
  2650. PROCEDURE GetFollowUp (REF gName: ARRAY OF CHAR; VAR followupTo : ARRAY OF CHAR): BOOLEAN;
  2651. (* Liefert das Followup fr eine Gruppe 
  2652.  *)
  2653.   VAR idx : INTEGER;
  2654. BEGIN
  2655.   IF findEntry (gName, idx)
  2656.   THEN
  2657.     IF groupArray^[idx].followup # NIL
  2658.     THEN
  2659.       MagicStrings.Assign (groupArray^[idx].followup^, followupTo);
  2660.       RETURN TRUE
  2661.     END;
  2662.   END;
  2663.   RETURN FALSE;
  2664. END GetFollowUp;
  2665.  
  2666. PROCEDURE GetNetName (VAR name: ARRAY OF CHAR);
  2667. (* Liefert zu einer Gruppe den Netzgruppennamen 
  2668.  *)
  2669.  VAR idx : INTEGER;
  2670. BEGIN
  2671.   IF findEntry (name, idx)
  2672.   THEN
  2673.     IF groupArray^[idx].netname # NIL
  2674.     THEN
  2675.       MagicStrings.Assign (groupArray^[idx].netname^, name);
  2676.     END;
  2677.   END;
  2678. END GetNetName;
  2679.  
  2680. PROCEDURE GetWriteAccess (REF name: ARRAY OF CHAR): BOOLEAN;
  2681. (* Liefert TRUE zurck, falls die Gruppe existiert und der 
  2682.  * Benutzer laut ITG schreiben darf
  2683.  *)
  2684.  VAR idx : INTEGER;
  2685. BEGIN
  2686.   IF findEntry (name, idx)
  2687.   THEN
  2688.     RETURN (groupArray^[idx].writeState = rwNA)
  2689.         OR (groupArray^[idx].writeState = rwOn)
  2690.         OR (groupArray^[idx].writeState = rwPossible);
  2691.   END;
  2692.   RETURN FALSE;
  2693. END GetWriteAccess;
  2694.  
  2695. (* Funktionen fr CatPutz: *)
  2696.  
  2697. PROCEDURE DeleteGroup (REF gName: ARRAY OF CHAR);
  2698. (* L”scht die Gruppe aus der Liste der in CAT verfgbaren Gruppen und 
  2699.  * pažt die Lesenummern an. Die Filenummern fr die Datenbank
  2700.  * mssen anders angepažt werden (z.B. mit SetNewCatNumber).
  2701.  * Ebenso muž das Gruppen.Pos extern angepažt werden.
  2702.  *)
  2703.   VAR idx : INTEGER;
  2704.       readnum : INTEGER;
  2705.       i     : INTEGER;
  2706. BEGIN
  2707.   IF findEntry (gName, idx)
  2708.   THEN
  2709.     readnum := groupArray^[idx].readNumber;
  2710.     groupArray^[idx].catNumber := -1;
  2711.     groupArray^[idx].readNumber := -1;
  2712.     listModified := TRUE;
  2713.     FOR i := 0 TO groups - 1 DO
  2714.       IF groupArray^[i].readNumber > readnum
  2715.       THEN
  2716.         DEC (groupArray^[i].readNumber);
  2717.       END;
  2718.     END;
  2719.   END; 
  2720. END DeleteGroup;
  2721.  
  2722. PROCEDURE SetNewCatNumber (REF gName: ARRAY OF CHAR; newNumber: INTEGER);
  2723. (* Setzt eine neue Gruppennummer fr eine Gruppe. 
  2724.  * Das GRUPPEN.INF wird danach neu geschrieben 
  2725.  *)
  2726.   VAR idx : INTEGER;
  2727. BEGIN
  2728.   IF findEntry (gName, idx)
  2729.   THEN
  2730.     groupArray^[idx].catNumber := newNumber;
  2731.     listModified := TRUE;
  2732.     (* Jetzt neues GRUPPEN.INF schreiben *)
  2733.     SaveGruppenInf();
  2734.   END;
  2735. END SetNewCatNumber;
  2736.  
  2737. BEGIN
  2738.   listModified := FALSE;
  2739.   maxGroups := 0;
  2740.   groups    := 0;
  2741.   groupArray := NIL;
  2742.   isSysop   := FALSE;
  2743. END GroupSelect.
  2744.